From 1bfa95910b550b822f065185bc2f530594814b53 Mon Sep 17 00:00:00 2001 From: Ales Rechtorik Date: Thu, 7 May 2026 14:06:26 +0200 Subject: [PATCH 01/24] 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 656e2e629..2eb42ac10 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 000000000..c8bfc10d9 --- /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 000000000..b143dfa1c --- /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 d0f8d0292..000000000 --- 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 000000000..ca738b4bf --- /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 000000000..a4e747cf3 --- /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 000000000..5801732e5 --- /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 000000000..611f319aa --- /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 000000000..78daf49b6 --- /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 000000000..6f2950c87 --- /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 000000000..11899f492 --- /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 000000000..370dba7a4 --- /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 000000000..88e6d5085 --- /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 000000000..ea071aa9a --- /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 000000000..25d6110e3 --- /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 000000000..1f239d9bf --- /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 000000000..9104b394b --- /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 000000000..db2380529 --- /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 000000000..7917e966a --- /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 000000000..84c1c9ab2 --- /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 000000000..476485917 --- /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 d7f27217a..114d0615f 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 1cf814036..0d82d497f 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 da54b907d..6ff6fcfba 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 198214baf34def57ecd1605f5dcf4bc94b207491 Mon Sep 17 00:00:00 2001 From: Ales Rechtorik Date: Thu, 7 May 2026 14:44:42 +0200 Subject: [PATCH 02/24] 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 c8bfc10d9..f5bb94991 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 b143dfa1c..ff463889d 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 ca738b4bf..dd2717fe5 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 a4e747cf3..6f4b38b1f 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 5801732e5..7d900655f 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 611f319aa..2b6923aa4 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 78daf49b6..9a43e1353 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 6f2950c87..fa6e56317 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 370dba7a4..a86265cda 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 88e6d5085..081549f86 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 ea071aa9a..c4f059689 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 25d6110e3..726ae24f6 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 1f239d9bf..057eb0ebe 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 9104b394b..a7a4187d1 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 db2380529..15364737e 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 84c1c9ab2..d4df0e798 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 0d82d497f..2910d12ef 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 6ff6fcfba..dbcccde39 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; From a8def99b910552f318f818a2201ea3607d1ba361 Mon Sep 17 00:00:00 2001 From: krls2020 Date: Sat, 9 May 2026 16:06:52 +0200 Subject: [PATCH 03/24] Refine ZCP docs for coding-agent workflow --- apps/docs/content/features/coding-agents.mdx | 115 +- .../features/local-remote-development.mdx | 62 +- .../docs/content/zcp/concept/how-it-works.mdx | 136 +- apps/docs/content/zcp/glossary.mdx | 74 +- apps/docs/content/zcp/overview.mdx | 81 +- apps/docs/content/zcp/quickstart.mdx | 93 +- .../content/zcp/reference/agent-workflow.mdx | 252 +- apps/docs/content/zcp/reference/index.mdx | 14 + .../content/zcp/reference/mcp-operations.mdx | 65 + .../content/zcp/reference/troubleshooting.mdx | 221 +- .../zcp/security/auditing-agent-work.mdx | 157 - .../zcp/security/production-policy.mdx | 35 +- .../security/tokens-and-project-access.mdx | 85 +- .../docs/content/zcp/security/trust-model.mdx | 105 +- .../content/zcp/setup/choose-workspace.mdx | 112 +- .../content/zcp/setup/hosted-workspace.mdx | 87 +- .../content/zcp/setup/local-agent-bridge.mdx | 83 +- .../zcp/workflows/build-and-verify-app.mdx | 106 +- .../content/zcp/workflows/build-with-zcp.mdx | 44 + .../workflows/create-or-adopt-services.mdx | 82 +- .../zcp/workflows/delivery-handoff.mdx | 76 +- .../zcp/workflows/package-running-service.mdx | 26 +- apps/docs/sidebars.js | 89 +- apps/docs/static/llms-full.txt | 3073 ++++++++--------- apps/docs/static/llms-small.txt | 2628 +++++++------- apps/docs/static/llms.txt | 23 +- zerops-llm-script.ts | 4 +- 27 files changed, 3521 insertions(+), 4407 deletions(-) create mode 100644 apps/docs/content/zcp/reference/index.mdx create mode 100644 apps/docs/content/zcp/reference/mcp-operations.mdx delete mode 100644 apps/docs/content/zcp/security/auditing-agent-work.mdx create mode 100644 apps/docs/content/zcp/workflows/build-with-zcp.mdx diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx index f5bb94991..43f1b1037 100644 --- a/apps/docs/content/features/coding-agents.mdx +++ b/apps/docs/content/features/coding-agents.mdx @@ -1,71 +1,69 @@ --- -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. +title: ZCP for Coding Agents +description: ZCP gives coding agents current project knowledge and bounded operations on one Zerops project — discover, deploy, verify, recover, and hand off. --- 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. +Coding agents need current project knowledge and a safe way to act on it. The hard part is rarely "write the function" — it's letting the agent connect product intent to infrastructure: services, env vars, logs, deploys, verification, 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. +**Zerops Control Plane (ZCP)** gives a coding agent current project state and bounded operations on one Zerops project. You describe the app or change you want; ZCP lets the agent discover services, use existing infrastructure or create what is missing, wire the app to managed services, deploy through the standard pipeline, verify the real endpoint, inspect logs, and report a URL or blocker. You hold the product intent, constraints, approvals, and quality bar. :::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). +ZCP runs with a project-scoped token that grants operational rights inside that project. Put it in a development or staging project, not the project serving production traffic. That keeps agent-driven changes away from production unless your CI or release process promotes them. 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. +Developers using a coding agent on application work that needs real infrastructure. The developer stays focused on the product outcome, technology choices, acceptance criteria, and judgment. ZCP gives the agent the platform context and operations needed to use or create services, connect the app to infrastructure, deploy, verify, recover from failure, and report back. -Today ZCP supports **Claude Code**, paired with a **Claude Pro or Max subscription**. Other MCP-aware agents will follow as their integrations stabilize. +The **Include Coding Agent** option bundles the currently supported agent CLI shown in the dashboard. Local setup can connect a compatible local agent client after `zcp init`. -## What ZCP MCP does +## What ZCP lets the agent do -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. +ZCP exposes a fixed set of operations on one Zerops project, grouped by user job. The prompt can stay focused on the app because the agent does not need you to describe what the project looks like - ZCP reports it from what is 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) | +| **Deploy** | Ship code through the standard build and deploy pipeline | [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) | +| **Verify** | Reachability + behavior checks against the actual URL | [Deploy, verify, and fix](/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) | +| **Handoff** | 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. +Behind one app request, the agent should read the project, use existing services or create missing ones, edit and deploy the app, verify the requested behavior, and then record how future changes should 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). +Full workflow terms: [Workflow terms](/zcp/reference/agent-workflow). ## Two ways to run it -ZCP MCP is a binary; the question is where it runs. +The `zcp` binary can run in a Zerops service or on your laptop. The user choice is remote setup or local setup.
- - 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) + + A Zerops service (`zcp@1`) inside the project runs ZCP. Enable **Include Coding Agent** to add the bundled agent CLI and preconfigure it to use ZCP. Enable Cloud IDE when you want browser VS Code. **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. [Set up →](/zcp/setup/local-agent-bridge) + + The `zcp` binary on your laptop, initialized in a project folder with `zcp init`. The agent runs in your local editor or CLI; private service access uses `zcli vpn up`. [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 the MCP 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 ZCP operations against the same project. Remote setup can package ZCP with an agent CLI and browser editor; local setup connects your own agent client to ZCP. -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). +Decision: [Choose remote or local setup](/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 +## What remote setup 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: +Remote setup starts with a `zcp@1` Zerops service. Two dashboard options decide what is bundled into that service: -**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. +**Include Coding Agent.** Adds the currently supported bundled agent CLI and preconfigures it to use ZCP running inside the `zcp@1` service. Agent authentication is handled separately by that agent's login method. -**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). +**Cloud IDE.** Browser-based VS Code (code-server) with 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 → Cloud IDE](/features/local-remote-development#cloud-ide-on-remote-setup). -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. +Without **Include Coding Agent**, the service can still exist, but there is no preconfigured agent waiting inside it. Without **Cloud IDE**, there is no browser editor. With both enabled, you get a one-click agent + IDE setup 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). +Both options are configured when you provision the service: [Set up remote ZCP → Path B](/zcp/setup/hosted-workspace#path-b--toggle-the-zcp-service). ## Why transparent infrastructure works for agents @@ -77,68 +75,49 @@ Agents reason over text. The choices Zerops makes for human transparency are als - 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. +ZCP is the ergonomic layer on top of Zerops APIs, `zcli`, and SSH for agents. It tightens the loop by packaging project state, guidance, operations, and verification behind one project-scoped interface. -## MCP and permissions +## Operations and permissions -ZCP MCP groups its operations into three categories so a host agent can gate them by policy: +ZCP groups its operations into three categories so an agent client or team policy can gate them:
- 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) + Inspect state, logs, events, configuration, and verification output. Safe to auto-allow. [Full list →](/zcp/reference/mcp-operations#read-only-operations) - - 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) + + Deploy, change env vars, manage lifecycle, scale, or delete services. Require team policy and confirmation where needed. [Full list →](/zcp/reference/mcp-operations#mutating-operations) - 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) + Set up infrastructure, mounts, imports, work sessions, or delivery configuration. Gated by team policy. [Full list →](/zcp/reference/mcp-operations#operational-setup)
-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). +That's the surface. The boundary is the project. The token authorizes one project, full stop — cross-project, account-wide, or organization actions stay outside reach. Integration tokens are still bounded by the permissions of the user who created them and by the project access selected for the token; 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). +Full operation list and permission semantics: [Advanced operations](/zcp/reference/mcp-operations). Token shape and confirmation gates: [Tokens and credentials](/zcp/security/tokens-and-project-access). -## Hacking on the hosted workspace +## Customize remote setup -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). +Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product - install packages, run additional processes alongside the agent, or ship a hardened version with team-standard tools baked in. Patterns: [Customize remote setup](/zcp/setup/hosted-workspace#customize-remote-setup). -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. +Local setup isn't extensible the same way; the binary on your laptop is just ZCP - 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 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 - -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. +When the agent runs in remote setup, 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. In local setup, 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). -**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. +## What ZCP is not -| | Persistent workspace | Real services | Deploy pipeline | Env parity | Standard tooling | -|---|:-:|:-:|:-:|:-:|:-:| -| One-shot generators | – | – | – | – | – | -| Replit | ✓ | ~ | ~ | – | – | -| E2B | – | – | – | – | ✓ | -| Codespaces / Gitpod | ✓ | – | – | – | ✓ | -| Cloudflare-style | – | ~ | ~ | – | – | -| **ZCP** | **✓** | **✓** | **✓** | **✓** | **✓** | +ZCP is not a prompt-to-prototype generator, an isolated code sandbox, or an editor-only cloud development environment. It is the control plane that gives a coding agent project-scoped infrastructure operations on Zerops: services, env vars, deploys, logs, verification, recovery, and delivery handoff. ## Where to start diff --git a/apps/docs/content/features/local-remote-development.mdx b/apps/docs/content/features/local-remote-development.mdx index ff463889d..452e0bf2e 100644 --- a/apps/docs/content/features/local-remote-development.mdx +++ b/apps/docs/content/features/local-remote-development.mdx @@ -1,14 +1,14 @@ --- 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. +description: Three ways to develop on Zerops — local with VPN, Cloud IDE, or your native IDE over SSH. Same project network, service types, and deploy pipeline; production stays separate. --- -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. +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. Across local VPN, Cloud IDE, and SSH, you use the same project network, hostnames, service types, and deploy pipeline. -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. +This is also what narrows the gap between development and production. Development and staging can use the same Zerops-managed service types, networking model, and `zerops.yaml` patterns production uses. Production should still live in a separate project with separate credentials, access rules, and resource policies. :::note -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. +Remote setup is a `zcp@1` service you can add to any project. It can provide a Linux dev container with a curated toolchain, optional Cloud IDE, and optional **Include Coding Agent** wiring. The modes below can use that service, connect directly to runtime services, or stay fully local over VPN; local development with `zcli vpn up` does not require ZCP at all. ::: ## How development on Zerops works @@ -17,11 +17,11 @@ 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. 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. +- **Local + VPN.** Your laptop joins the private network via WireGuard. Editor, toolchain, and processes stay where they are. Remote setup isn't required. +- **Cloud IDE.** 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 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. +Same `db:5432`, same `api:3000`, same project credentials in all three. Switch modes without changing anything about the development project. ## Local + VPN @@ -37,9 +37,9 @@ 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: 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. +What you don't have to do: add remote setup, 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 service type as stage or production. -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. +This mode is also where coding agents running on your laptop fit. Without ZCP, a local agent client inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. To let the agent operate the project - discover, deploy, verify, read logs, and manage delivery handoff - set up [local ZCP](/zcp/setup/local-agent-bridge). **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. @@ -47,13 +47,13 @@ This mode is also where coding agents running on your laptop fit. A local Claude See the [VPN reference guide](/references/networking/vpn). ::: -## Browser Cloud IDE on ZCP +## Cloud IDE on remote setup **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 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. +When you add remote setup (a `zcp@1` service) to a project — see [Set up remote ZCP](/zcp/setup/hosted-workspace) — you get an Ubuntu-based dev container with `zcp`, `zcli`, GitHub CLI, database CLIs, shell utilities, browser automation tools, and SSHFS. Enable **Cloud IDE** for browser-based VS Code; enable **Include Coding Agent** for the bundled agent CLI plus ZCP wiring. -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: +Remote setup can also mount 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,14 +61,14 @@ 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 hosted workspace can mount multiple dev services at once, covering 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 Zerops deploy pipeline. One remote setup service 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.** The hosted workspace isn't on your laptop, so your local SSH keys don't follow it. Two patterns work: +**Source control.** Remote 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 the `zcp` service's secret environment variables; `git` reads it through a credential helper. @@ -87,7 +87,7 @@ ssh frontenddev ssh zcp # if you provisioned the `zcp` service ``` -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. +In this mode, remote setup 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. Remote setup becomes a *convenience* layer: a single container that carries the toolchain, mounts multiple dev services over SSHFS, and can include `zcli`, Cloud IDE, and coding-agent wiring for the project. If you'd rather connect your editor directly to your runtime services and skip remote setup 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 the `zcp` service) uses your laptop's GitHub key over the forwarded agent socket — no token storage required. @@ -104,43 +104,43 @@ See the [SSH reference guide](/references/networking/ssh). ## Picking a mode -| | Local + VPN | Cloud IDE on ZCP | Native IDE over SSH | +| | Local + VPN | Cloud IDE | Native IDE over SSH | |---|---|---|---| | Editor runs | Local | Browser | Local | -| Toolchain | Local | Hosted workspace | Hosted workspace or service container | +| Toolchain | Local | Remote ZCP | Remote ZCP or service container | | Dev databases | Hostname via VPN | Native, on private network | Native, on private network | -| Hosted workspace required | No | Yes | No (convenience) | +| Remote ZCP 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 the hosted workspace 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 remote setup 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). +For the agent-driven path, the setup decision (remote `zcp@1` service vs. local `zcp` binary) is on its own page: [Choose remote or local setup](/zcp/setup/choose-workspace). -## Same setup, dev to production +## Same architecture, separate 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.yaml`. A dev project, a stage project, and a production project differ in resource allocation and access rules, not in architecture. +The development environments above don't approximate production by inventing a different platform. A ZCP development project can run dev and stage services with the same Zerops-managed service types, private networking, load-balancer model, and `zerops.yaml` patterns that production uses in its separate project. Dev, stage, and production differ in resource allocation, access rules, and credentials, 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 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 and stage, you've already tested it against the kind of infrastructure it'll meet in production. The remaining differences are scale, credentials, access policy, and data. -This applies whether the developer is human or an agent — see [Infrastructure for Coding Agents](/features/coding-agents) for the agent case. +This applies whether the developer is human or an agent — see [ZCP for Coding Agents](/features/coding-agents) for the agent case. ## How this differs from cloud IDEs -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. +If you've used GitHub Codespaces or Gitpod, Cloud IDE 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.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. +**Dev, staging, and production use the same platform model.** 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 model: managed Postgres, private networking, and `zerops.yaml`. Production still lives in its own project with its own credentials and policies. -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. +Remote ZCP 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 - 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 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 +- Coding agents on Zerops → [ZCP for Coding Agents](/features/coding-agents) +- Set up remote setup → [Set up remote ZCP](/zcp/setup/hosted-workspace) +- Pick remote or local ZCP → [Choose remote or local setup](/zcp/setup/choose-workspace) +- Term reference → [ZCP Glossary](/zcp/glossary) diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx index dd2717fe5..e5d32cf1f 100644 --- a/apps/docs/content/zcp/concept/how-it-works.mdx +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -1,105 +1,73 @@ --- 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." +description: "How product intent moves through project discovery, infrastructure, app changes, deployment, verification, and delivery." --- -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. +ZCP turns product or app intent into a verified deployed app by giving the coding agent two things it normally lacks: current knowledge of the Zerops project and the tools to change, deploy, inspect, and verify it. -## Where ZCP runs +## The model in one pass -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. +Product intent enters a project loop: read current state, decide whether the needed services already exist, change the app and its Zerops wiring, deploy, verify from evidence, and return either proof or a concrete blocker. ```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 +flowchart TD + intent["Product intent
Build a task board where tasks stay saved after refresh."] + context["ZCP context
live services, env vars, logs, events,
runtime guidance, available operations"] + target["Project fit
use existing services or create missing ones;
select the app runtime,
not the zcp setup service"] + app["Build the app
code, zerops.yaml, env wiring"] + deploy["Deploy through Zerops"] + verify{"Verify real behavior
endpoint or UI"} + fix["Read evidence
logs, events, checks"] + done["Done
URL, endpoint, or UI proof"] + blocker["Blocker
missing credential, decision,
or repeated failure"] + delivery["Future delivery
direct deploy, git push, or CI handoff"] + + intent --> context --> target + target --> app --> deploy --> verify + verify -->|fixable| fix --> app + verify -->|verified| done --> delivery + verify -->|needs human| blocker + + classDef user fill:#f7fbff,stroke:#2d72d9,stroke-width:1.5px,color:#172033; + classDef zcpbox fill:#eef7f1,stroke:#32845a,stroke-width:1.5px,color:#172033; + classDef work fill:#fffaf0,stroke:#c47f17,stroke-width:1.5px,color:#172033; + classDef done fill:#f5f3ff,stroke:#7157d9,stroke-width:1.5px,color:#172033; + classDef stop fill:#fff1f2,stroke:#d33f49,stroke-width:1.5px,color:#172033; + + class intent user; + class context zcpbox; + class target,app,deploy,verify,fix work; + class done,delivery done; + class blocker stop; ``` -- **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 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, 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.** 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 - -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 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, the MCP returns after the push lands and the agent polls events until the runtime is active and records the deploy. +The important signals are simple: the agent read current project state, named the target runtime, verified the requested behavior, and explained what remains. -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 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 the MCP for status and resumes from current platform state. Recovery doesn't depend on the prior chat. - -### Hand off +## Where ZCP runs -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). +ZCP can run in two places: -For a different kind of handoff — turning a deployed service into reusable import files — see [Package a running service](/zcp/workflows/package-running-service). +| Path | What runs where | What changes for you | +|---|---|---| +| Remote setup | A `zcp@1` service runs the `zcp` binary inside the Zerops project. **Include Coding Agent** adds the bundled agent CLI and preconfigures it to use ZCP; **Cloud IDE** adds browser VS Code. | The work stays inside the project. The agent can use project-private networking and SSHFS mounts for runtime files. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your editor's agent talks to it. | Your checkout and dev server stay local. Managed services are reached over VPN, and `.env` generation bridges project credentials into your local app. | -## Project boundary and confirmation gates +The project-scoped control plane is the same idea in both paths. The filesystem, network path, deploy source, and safety profile are different. See [Choose remote or local setup](/zcp/setup/choose-workspace). -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). +## What ZCP reads -Two operations carry an explicit confirmation gate because the loss isn't reversible from inside the conversation: +ZCP reads live project state instead of relying on a long prompt: -- **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. +- services and whether they are runtime or managed dependencies, +- runtime layout, such as one app runtime, a dev+stage pair, or a local checkout linked to a Zerops runtime, +- service env-var keys and references, +- build/deploy events, runtime logs, and verification results, +- the current work state when a session is interrupted. -Detail: [Tokens and credentials → Confirmation gates](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions). +Recovery starts from live state. If a session gets confused or interrupted, ask the agent to read ZCP status before changing anything else. -## ZCP MCP and zCLI +## What counts as done -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. +A task is not done when code is written, or when a build succeeds. A ZCP task is done when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the URL or a concrete blocker. -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. +Workflow path: [Build with ZCP](/zcp/workflows/build-with-zcp). Exact terms and runtime layouts: [Workflow terms](/zcp/reference/agent-workflow). diff --git a/apps/docs/content/zcp/glossary.mdx b/apps/docs/content/zcp/glossary.mdx index 6f4b38b1f..ef24d32f6 100644 --- a/apps/docs/content/zcp/glossary.mdx +++ b/apps/docs/content/zcp/glossary.mdx @@ -1,66 +1,72 @@ --- 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." +description: "Short definitions for ZCP terms used across the docs." --- -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. +## Core names -## What ZCP is +**ZCP** - Zerops Control Plane for coding agents: the project-scoped control plane documented in this section. -**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 operations** - the project-scoped operations surface exposed to a coding agent. In MCP clients, this appears as the ZCP MCP server. -**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 inside remote setup or on your laptop in local setup. -**`zcp` binary** — the executable. Runs as a process inside the hosted service, or on your laptop in the local agent bridge. +**zCLI** - the Zerops command-line client for humans, scripts, VPN, and CI. It coexists with ZCP. + +**zsc** - the in-container Zerops Setup Control utility used from `zerops.yaml`. ## 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). +**Remote setup** - ZCP running inside a Zerops `zcp@1` service in the project. + +**`zcp@1` service** - the Zerops service type behind remote setup. + +**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI and preconfigures it to use the project's ZCP operations. + +**Cloud IDE** - browser-based VS Code served by remote setup. -**`zcp@1`** — the Zerops service type the hosted workspace runs as. The wrapping service, not the MCP itself. +**AI Agent environment** - a recipe environment preset that creates app services plus remote setup with **Include Coding Agent** enabled. It is a recipe preset, not a separate ZCP setup path. -**`zcp` service** — an instance of `zcp@1` in a specific project. What you see in the Zerops dashboard. +**Local setup** - the `zcp` binary running on your laptop after `zcp init`, while your editor or CLI agent talks to it. -**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). +**Local agent bridge** - local setup with the `zcp` binary connected to a local agent client. -**Cloud IDE** — the browser-based VS Code (code-server) the hosted workspace can serve. Optional toggle on the `zcp` service. +## Workflows and results -**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. +**Service setup** - the infrastructure phase behind an app prompt. It uses existing runtime and managed services when they fit, creates missing services when needed, then stops before app code, `zerops.yaml`, or deploy. -## What the agent does +**Deploy, verify, and fix** - the app phase. It scopes the runtime, changes code/config, deploys, verifies reachability, verifies requested behavior, and fixes failures. -**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). +**Runtime scope** - the app runtime the change targets, such as `appdev`, `appstage`, `app`, or a linked local target. -**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. +**Delivery choice** - what happens to future changes after a verified deploy: direct deploy, git-push, or external handoff. -**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). +**ZCP status** - a live project read the agent can use after interruption or confusion. Ask for it before letting the agent change anything else. -**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. +**Blocker** - a clear stop state with evidence: failure category, runtime in scope, what was tried, and what decision or credential is needed. -**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). +## Project and runtime terms -**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. +**Runtime service** - a service that runs app code. -## What the project looks like +**Managed service** - database, cache, queue, search, storage, or similar dependency. It provides connection details; it is not an app deploy target. -**Runtime scope** — the service the agent targets for a change (`appdev`, `appstage`, `app`). +**Runtime layout** - which app runtime services ZCP should use: -**Workspace shape** — ZCP's name for the project topology: +- `standard` - dev runtime plus explicit stage runtime. +- `dev` - one mutable development runtime. +- `simple` - one runtime with no dev/stage split. +- `local-stage` - local checkout linked to one Zerops runtime as deploy target. +- `local-only` - local checkout with no linked runtime yet. -- `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 +**Service scaling mode** - Zerops scaling setting such as `HA` or `NON_HA`. Different from runtime layout. -Decision context: [Choose your workspace → Workspace shapes](/zcp/setup/choose-workspace#workspace-shapes). +**Stage** - review or promotion target. Stage is not production. -**Service mode** — Zerops scaling shape (`HA`, `NON_HA`). Independent of workspace shape — see [Scaling](/features/scaling). +## Credentials -**Recipe** — a Zerops-published starter project. With the **AI Agent** environment selected, recipes provision a `zcp@1` service alongside the runtime services. +**`ZCP_API_KEY`** - project-scoped Zerops token used by ZCP. -## Three names that get confused +**`GIT_TOKEN`** - git provider credential used by remote git-push delivery when needed. -- **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. +**`ZEROPS_TOKEN`** - Zerops token commonly used by GitHub Actions or external CI that runs `zcli`. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index 7d900655f..d36340490 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -1,34 +1,75 @@ --- -title: "Zerops Control Plane — section overview" -description: "What ZCP docs cover, where to start, and what stays in the platform's canonical references." +title: "Zerops Control Plane for coding agents" +description: "What ZCP gives a coding agent, where it runs, and what a finished Zerops app task should prove." --- -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 the Zerops control plane setup that lets a coding agent work from real project state instead of a hand-written operations checklist. It gives the agent current project knowledge, guarded project operations, deploy/verify evidence, and Zerops guidance for connecting an app to its infrastructure. -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). +It can run remotely inside a `zcp@1` service with **Include Coding Agent** enabled, or locally as the `zcp` binary initialized beside your CLI/editor agent. In both cases, the developer stays focused on product intent, technology choices, acceptance criteria, and decisions that require human judgment. + +## What ZCP does + +ZCP makes four things available to the agent inside a normal Zerops project: + +**Project awareness.** The agent can read services, runtime layout, env vars, logs, events, and current work state instead of asking you to describe the project. + +**Infrastructure wiring.** The agent can separate the `zcp` setup service, app runtime services, and managed dependencies, then connect the app to the right env vars and service references. + +**Deploy and verify loop.** The agent can prove the app works on the selected runtime, not stop after writing code or seeing a green build. + +**Delivery choice.** After verified work, the agent can keep deploying directly, push to git, or use external handoff to your CI or a human. + +You provide product intent and judgment: what to build, which runtime or stage matters when you care, and what result counts as done. + +## A good ZCP session + +A ZCP-backed session should be product-led and evidence-based: + +- The agent reads the project before changing it. +- It uses existing services when they fit and creates missing infrastructure when they do not. +- It deploys and verifies the requested behavior on the real endpoint or UI. +- It ends with a URL or proof of behavior, or with a blocker that names the missing decision, credential, or repeated failure. + +## What you no longer have to do + +ZCP is valuable because it removes the operational checklist from the prompt. You should not have to: + +- describe every service already in the project, +- decide which env-var references connect the app to managed services, +- copy database credentials, logs, or deploy timelines into chat, +- repeat deploy, verify, log-reading, and URL-reporting instructions on every app task, +- translate a failed build or broken route into a guess before the agent can act, +- choose internal workflow names before describing what you want built. + +You still own the product intent, technology constraints, acceptance criteria, credentials that live outside Zerops, and approval for destructive actions. ## Where to start -| If you want to… | Go to | +| Goal | Page | |---|---| -| 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) | +| Try ZCP once with no local install | [Quickstart](/zcp/quickstart) | +| Understand the model before setup | [How ZCP works](/zcp/concept/how-it-works) | +| Choose remote vs local | [Choose remote or local setup](/zcp/setup/choose-workspace) | +| Add remote setup | [Set up remote ZCP](/zcp/setup/hosted-workspace) | +| Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | +| Build or change an app | [Build with ZCP](/zcp/workflows/build-with-zcp) | | 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) | +| Understand tokens and boundaries | [Trust model](/zcp/security/trust-model) | +| Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | +| Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | +| Diagnose a stuck session | [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. +ZCP is a control plane for agents, not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, scaling, and service references remain canonical for platform behavior. + +## Names that matter -## Three names that get confused +- **ZCP** - Zerops Control Plane in its coding-agent setup. +- **Remote setup** - a `zcp@1` service inside the Zerops project. The service runs the `zcp` binary and exposes ZCP to the agent from inside the project. +- **Local setup** - the `zcp` binary installed on your machine and initialized in a project folder with `zcp init`. +- **Include Coding Agent** - the remote setup option that adds the bundled agent CLI and preconfigures it to use ZCP in the service. +- **Cloud IDE** - optional browser-based VS Code in remote setup. +- **`zcp` binary** - the executable. In remote setup it is inside the `zcp@1` service; in local setup you install it and run `zcp init` in a project folder. -- **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. +For the full vocabulary, including `zcp`, zCLI, zsc, MCP, runtime layouts, and credential names, see the [Glossary](/zcp/glossary). diff --git a/apps/docs/content/zcp/quickstart.mdx b/apps/docs/content/zcp/quickstart.mdx index 2b6923aa4..df25088d1 100644 --- a/apps/docs/content/zcp/quickstart.mdx +++ b/apps/docs/content/zcp/quickstart.mdx @@ -1,83 +1,70 @@ --- -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." +title: "Quickstart" +description: "Open remote setup, ask for a small app, and inspect the verified result." --- -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. +Remote setup is the fastest first run: no local `zcp` install, no local MCP config, and the bundled agent is already connected to the project. You will open a project with **Include Coding Agent** enabled, give the agent a small app request, and inspect what it verified. ## 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 +- A Zerops account with permission to create a project. +- A login for the bundled agent shown in the dashboard. -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. +## Create a remote setup 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 fastest path is a recipe with the **AI Agent** environment: -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: +1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes). +2. Pick a recipe that matches the stack you want to try. +3. Select the **AI Agent** environment. +4. Deploy the recipe. -- `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 +The **AI Agent** environment creates the app services plus a `zcp@1` remote setup with **Include Coding Agent** enabled. Keep **Cloud IDE** enabled too so you can open the browser workspace. If you start from an empty project instead, enable **Add Zerops Control Plane (ZCP) service** during project creation and keep **Include Coding Agent** on; the agent can use existing app services or create the missing ones from your prompt. Full setup path: [Set up remote ZCP](/zcp/setup/hosted-workspace). -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 remote setup -## Open the workspace +When provisioning finishes, open the `zcp` service in the Zerops dashboard. If **Cloud IDE** is enabled, open its workspace URL; a browser editor opens with the agent connected to this project through ZCP. -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. +Ask for what the app should do. Service setup, deploy, verification, and reporting are part of the ZCP-backed agent contract. -## 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: +In the agent chat: ```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. +Build a task board where tasks stay saved after refresh. ``` -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. +Add constraints only when they change the product, runtime layout, or delivery path: -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 +```text +Build a task board where tasks stay saved after refresh. Use the existing dev+stage pair if this project has one. +``` -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. +```text +After it works, set up git-push delivery to git@github.com:my-org/task-dashboard.git. +``` -Quick checks: +## What you should see -- 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. +A good run should make these points clear: -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. +1. The agent read current project state before changing anything. +2. It named the app runtime it will change. The `zcp` service is the ZCP setup, not the app target. +3. It used existing services when they already fit, or created missing services before app work. +4. It changed code and `zerops.yaml` as needed. +5. It deployed through Zerops and read logs or events if something failed. +6. It verified both platform reachability and the requested dashboard behavior. +7. It ended with a URL or a concrete blocker. -If something is missing or broken, see [Troubleshooting](/zcp/reference/troubleshooting). +Open the URL the agent gives you. If the page loads but the requested behavior is missing, the task is not done; ask the agent to verify that exact behavior again. ## 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. +- [How ZCP works](/zcp/concept/how-it-works) - the model behind the run. +- [Build with ZCP](/zcp/workflows/build-with-zcp) - how real app work flows. +- [Choose how finished work ships](/zcp/workflows/delivery-handoff) - direct deploy, git-push, or external handoff. ## Gotchas -- **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). +- **Deploy success is not done.** The app behavior you requested must be verified on the real URL or endpoint. +- **The `zcp` service is not the app.** Runtime services such as `appdev`, `appstage`, or `app` receive app code and deploys. +- **Production stays out of the agent loop.** Use ZCP in dev/staging projects; promote to production through your CI or release process. diff --git a/apps/docs/content/zcp/reference/agent-workflow.mdx b/apps/docs/content/zcp/reference/agent-workflow.mdx index 9a43e1353..f7c469a04 100644 --- a/apps/docs/content/zcp/reference/agent-workflow.mdx +++ b/apps/docs/content/zcp/reference/agent-workflow.mdx @@ -1,215 +1,91 @@ --- -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. +title: "Workflow terms" +description: "Precise ZCP workflow terms: session layers, service setup, app completion, runtime layouts, delivery choices, failure categories, and completion evidence." --- -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. +Exact vocabulary for ZCP workflows: runtime layout, verification, delivery, failure state, and completion evidence. Day-to-day app prompts should use outcomes; policies and handoffs can use these names when precision matters. -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). +## Session layers -## 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 | +| Layer | What it is | What changes here | |---|---|---| -| `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. +| ZCP setup | Where ZCP runs: remote `zcp@1` service or local `zcp` binary. | No app code should be deployed to the setup itself. | +| Target runtime service | The app runtime: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys. | +| Managed services | Databases, caches, queues, search, storage. | Schema/data operations, never app code deploys. | -## Verification layers +The `zcp` service is the control surface, not the app runtime. -| 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. | +## Service setup -Both must pass before a session reports completion. +Before code work starts, ZCP should identify the app runtime services and managed dependencies. It should use existing services when they fit, create missing services when the project is empty, and resume from live project state if setup was interrupted. -## 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 | +The service setup phase stops before app edits begin; the full ZCP work session then continues into code, deploy, verification, and recovery. -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. +## App work completion contract -Recovery moves and symptom→category mapping: [Troubleshooting](/zcp/reference/troubleshooting). +For an app intent, ZCP expects the agent to handle code, config, deploy, verify, and retries behind the prompt. A completed session leaves evidence of: -## Workspace shapes +- runtime scope, +- app code and `zerops.yaml` changes, +- a direct deploy for the first verified runtime, +- platform reachability, +- requested behavior on the real endpoint or UI, +- evidence-based retries when something failed, +- a URL or blocker. -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. +## Verification layers -| Contract | Meaning | +| Layer | What it proves | |---|---| -| `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. +| Runtime reachability | Service status, recent error logs, and HTTP probe for eligible runtime services. | +| Requested behavior | The user-requested endpoint, UI state, job result, database state, or stage URL works. | -Plain-language framing of the same choice: [Choose how finished work ships](/zcp/workflows/delivery-handoff). +Both layers must pass before a session reports completion. -## MCP tool surface +## Runtime layouts -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 | +| Layout | Meaning | |---|---| -| `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 MCP-side guidance specific to current project state and step | +| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage must be named when included. | +| `dev` | One mutable development runtime. | +| `simple` | One runtime with no dev/stage split. | +| `local-stage` | Local checkout linked to one Zerops runtime for deploys. | +| `local-only` | Local checkout with no linked deploy target yet. | -### Destructive — mutate the project or its services +Runtime layout describes which app runtime services ZCP should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. -Host agent should require user confirmation. Two operations carry an explicit MCP-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 | +## Delivery choice -### Operational — set up or coordinate work +Delivery choice applies after a verified deploy and describes future changes. -Host agent decides per team policy. These don't fit cleanly as read-only or destructive; they're setup-shaped. - -| Tool | Purpose | +| Choice | Meaning | |---|---| -| `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 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. 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. +| Direct deploy | ZCP keeps deploying changes directly to the target runtime. | +| Git push | The agent commits and pushes to a configured remote; any resulting build still needs verification. | +| External handoff | Your CI, release process, or a human owns future delivery. | -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. +## Failure categories -Step-by-step audit playbook with the evidence-coverage matrix and take-over decision tables: [Auditing agent work](/zcp/security/auditing-agent-work). +| Category | Meaning | Read first | +|---|---|---| +| `build` | Build phase failed. | Build logs and build commands. | +| `start` | Build passed, runtime failed to start or crashed. | Runtime/prepare logs and start command. | +| `verify` | Runtime exists but reachability or behavior failed. | Failing check detail and runtime logs at request time. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and network path. | +| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Structured rejection detail and config files. | +| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | +| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | + +## Completion evidence + +A well-run session can answer: + +- which services were used or created, +- which runtime scope was changed, +- which deploy passed, +- which reachability and behavior checks passed, +- which URL or endpoint proves the result, +- which delivery contract applies next, +- or which blocker remains and what evidence supports it. + +Operation-name reference lives in [Advanced operations](/zcp/reference/mcp-operations). Recovery shortcuts live in [Troubleshooting](/zcp/reference/troubleshooting). diff --git a/apps/docs/content/zcp/reference/index.mdx b/apps/docs/content/zcp/reference/index.mdx new file mode 100644 index 000000000..8ded40b38 --- /dev/null +++ b/apps/docs/content/zcp/reference/index.mdx @@ -0,0 +1,14 @@ +--- +title: "Reference" +description: "Precise ZCP terms, advanced operations, recovery shortcuts, and glossary." +--- + +Exact terms, operation names, and recovery detail live here. Day-to-day app work starts in [Build with ZCP](/zcp/workflows/build-with-zcp). + +| Need | Page | +|---|---| +| Name the workflow phases, runtime layouts, verification layers, and delivery choices | [Workflow terms](/zcp/reference/agent-workflow) | +| Look up common MCP operation names for agent-client policy or debugging | [Advanced operations](/zcp/reference/mcp-operations) | +| Turn a running service into a re-importable bundle | [Package a running service](/zcp/workflows/package-running-service) | +| Recover a stuck or confused session | [Troubleshooting](/zcp/reference/troubleshooting) | +| Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | diff --git a/apps/docs/content/zcp/reference/mcp-operations.mdx b/apps/docs/content/zcp/reference/mcp-operations.mdx new file mode 100644 index 000000000..4be9ecb0a --- /dev/null +++ b/apps/docs/content/zcp/reference/mcp-operations.mdx @@ -0,0 +1,65 @@ +--- +title: "Advanced operations" +description: "Common ZCP operation names for app-building, debugging, and automation." +--- + +Most users never need operation names. They matter when you configure agent-client policy, debug an MCP integration, or automate ZCP directly. In normal app work, ask the agent for outcomes. Recipe-authoring and ZCP-internal maintenance operations are intentionally out of scope. + +## Permission classes + +| Class | Meaning | +|---|---| +| Read-only | Inspect state, logs, events, guidance, or verification output. | +| Mutating | Change services, env vars, scaling, deploy state, or lifecycle. | +| Operational setup | Coordinate workflows, mounts, imports, recipes, delivery setup, or generated local files. | + +## Read-only operations + +| Operation | Purpose | +|---|---| +| `zerops_discover` | Read services, ports, env-var keys, and current state. | +| `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | +| `zerops_events` | Read service-scoped project activity, deploys, builds, scaling, and failures. | +| `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | +| `zerops_knowledge` | Fetch ZCP guidance or platform knowledge for the current state. | +| `zerops_process` | Check a known async process; cancel is a mutating action. | + +## Mutating operations + +| Operation | Purpose | +|---|---| +| `zerops_deploy` | Ship code through the Zerops build and deploy pipeline. | +| `zerops_env` | Read, set, delete, or generate env vars and local `.env` files. | +| `zerops_manage` | Start, stop, restart, reload, or connect storage. | +| `zerops_scale` | Change CPU, RAM, disk, CPU mode, or container autoscaling where supported. `HA`/`NON_HA` is set at service creation. | +| `zerops_subdomain` | Enable or disable public subdomain access. | +| `zerops_delete` | Delete a service. Explicit named approval is required. | + +## Operational setup + +| Operation | Purpose | +|---|---| +| `zerops_workflow` | Track, recover, or configure ZCP work sessions; also carries the package-running-service export flow. | +| `zerops_import` | Import project/service definitions. Destructive override is gated. | +| `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | +| `zerops_preprocess` | Expand Zerops preprocessor expressions. | +| `zerops_export` | Read platform project/service export YAML and service metadata. | + +## Remote and local differences + +| Area | Remote setup | Local setup | +|---|---|---| +| Files | Runtime files through SSHFS/project containers | Local working directory | +| Deploy source | Project service or batch deploy inside Zerops | Local `workingDir` | +| Dev server | ZCP can run/inspect project-container dev processes | Your local tool owns the dev server | +| Env bridge | Project env is available in the project | `.env` generation resolves project references for local app use | +| Git | Workspace-managed credentials | User's local git credentials | + +## Confirmation gates + +Two operations require explicit care: + +- **Service deletion** requires explicit user approval in the current conversation, by service name. +- **Destructive import override** first refuses and names what would be replaced; a second call must acknowledge the same targets. + +See [Tokens and credentials](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions) for the user-facing confirmation flow. diff --git a/apps/docs/content/zcp/reference/troubleshooting.mdx b/apps/docs/content/zcp/reference/troubleshooting.mdx index fa6e56317..2134aab48 100644 --- a/apps/docs/content/zcp/reference/troubleshooting.mdx +++ b/apps/docs/content/zcp/reference/troubleshooting.mdx @@ -1,210 +1,51 @@ --- title: "Troubleshooting" -description: "Diagnose ZCP problems by failure category or by symptom — deploys, logs, public URLs, tokens, VPN, workflow state." +description: "Fast recovery for ZCP sessions: read status first, use failure categories, and follow the right evidence." --- -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: +Start recovery from current project state, not from chat memory. ```text -Show me the build logs for the last failed deploy of appdev, and read the failure classification. +Read ZCP status and tell me where this project stands before changing anything. ``` -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 +That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. -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: +## Failure categories -| Symptom | Likely cause | Next move | +| Category | Meaning | First 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. | +| `build` | Build failed before a runtime could start. | Read build logs and `zerops.yaml` build steps. | +| `start` | Build passed, runtime failed to start or crashed. | Read runtime/prepare logs and check start command, ports, and env vars. | +| `verify` | Runtime exists, but reachability or requested behavior failed. | Read the failing check, HTTP response, and runtime logs at request time. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Check the named network path and, for local setup, VPN. | +| `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Read the structured rejection and fix the named field. | +| `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | Fix the named credential; do not rotate unrelated tokens. | +| `other` | No known category matched. | Read raw events/logs and stop if the same unknown failure repeats. | -### Public URL errors +## Common symptoms -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. 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 | -|---|---|---| -| `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. | +| Symptom | Likely move | +|---|---| +| Build log shows missing command/module/lock file | Fix build commands, manifests, deploy files, or dependencies. | +| Runtime is `READY_TO_DEPLOY` or restarts continuously | Read runtime logs; the start command or env contract is usually wrong. | +| Public URL returns 502 or connection error | Check port binding, `0.0.0.0`, subdomain readiness, and whether the service is HTTP-eligible. | +| Deploy succeeded but feature is broken | Treat it as behavior verification failure, not deploy success. | +| Local app cannot reach `db`, `redis`, or storage | Bring VPN up and regenerate `.env` if project env changed. | +| Agent targets `zcp` as the app | Correct it to the runtime service. | +| Agent loops on the same failure | Ask for category, evidence read, fixes attempted, and the next human decision. | -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) - -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`. | +Public subdomain access is enabled on first deploy for eligible HTTP runtimes. Workers and non-HTTP services do not get a useful public URL by design. -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). +## When to stop -## Gotchas +Stop the loop when the agent cannot make progress with new evidence, needs a missing credential, is about to delete or replace a service, or the target runtime/stage is ambiguous. -- **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. +Before destructive recovery, read service-scoped events, logs, deploy/verify result, and git history when delivery uses git-push. ## 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 the MCP reads from. +- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) +- [Workflow terms](/zcp/reference/agent-workflow) +- [Tokens and credentials](/zcp/security/tokens-and-project-access) +- [Set up local ZCP](/zcp/setup/local-agent-bridge) diff --git a/apps/docs/content/zcp/security/auditing-agent-work.mdx b/apps/docs/content/zcp/security/auditing-agent-work.mdx deleted file mode 100644 index 11899f492..000000000 --- a/apps/docs/content/zcp/security/auditing-agent-work.mdx +++ /dev/null @@ -1,157 +0,0 @@ ---- -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 index a86265cda..07f2d2c64 100644 --- a/apps/docs/content/zcp/security/production-policy.mdx +++ b/apps/docs/content/zcp/security/production-policy.mdx @@ -3,34 +3,32 @@ 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. +Rule: keep ZCP in a development or staging Zerops project. Production should be a separate project without a `zcp` service; promotion happens through CI or a release pipeline with production-scoped credentials. -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. +The platform doesn't stop you from adding a `zcp` service to a production project. Treat the dev/staging boundary as load-bearing because crossing it puts an agent inside production, not because Zerops will refuse the configuration. ## 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. +The clean separation is the project boundary: ZCP runs where the agent works, while production runs in its own project. -Why a separate project: - -- **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. +- **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. +- **Scaling and backup policies differ.** Production typically runs HA 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 MCP runs and the agent works, and a production project with no `zcp` service, 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 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 -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. +Stage isn't a formality. It's where the agent proves the change works on the same architecture pattern production uses — same runtime version, same managed service types, same build/deploy path — before promotion to the separate production project. -Use stage as the production-shape rehearsal: +Use stage as the production-like rehearsal: -- The agent develops on dev (`appdev` in the `standard` workspace shape). +- The agent develops on dev (`appdev` in the `standard` runtime layout). - 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. +Skip stage and you skip the layer where production-like failures show up. ## Promotion path @@ -40,7 +38,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. 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. +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). @@ -48,16 +46,15 @@ Broader CI/CD reference: [CI/CD with Zerops](/guides/ci-cd). | Anti-pattern | Why it's wrong | |---|---| -| Add a `zcp` service to your production project | The agent gets project-scoped power over production | +| Add a `zcp` service to your production project | The agent gets project-scoped operational access to 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 | +| Treat dev verification as production approval | Dev is hot-reload; stage is the production-like 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 ZCP setup | ## 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. +- [Troubleshooting](/zcp/reference/troubleshooting) — recover or take over 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. +- [Deploy, verify, and fix](/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 index 081549f86..2b1ebc627 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 MCP enforces before destructive actions." +description: "Project-scoped Zerops tokens, git credentials, and the confirmation gates ZCP enforces before destructive actions." --- -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). +ZCP enforces the project boundary at three layers: token shape (one project, exactly), where the token lives (platform-injected for remote setup, in `.mcp.json` for local setup), and confirmation gates on operations that are not reversible from inside the conversation. -## Required token shape — one project, full access +## Recommended token shape — one project, full access -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. +ZCP requires a Zerops API token that resolves to **exactly one project** at startup. For normal agent workflows, use **Custom access per project** scoped to a single project with **Full access**. Read-only tokens can pass startup, but they fail as soon as the agent tries to deploy, write env vars, or operate services. To generate one: @@ -21,36 +21,29 @@ The token's blast radius equals the project. Other projects in the organization, ## Rejected token shapes -The MCP validates the token at startup and refuses the wrong shapes: +ZCP validates the token at startup and refuses wrong shapes before the agent can operate the project. -| Token shape | What the MCP does | Why | +| Token shape | What ZCP does | Why | |---|---|---| -| 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. | +| Multi-project, including account-wide full access | Refuses to start | One ZCP process equals one project; ZCP won't pick which one. | +| No project access | Refuses to start | 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. | +| Expired or revoked | Refuses to start | The token no longer authenticates against the Zerops API. | -A multi-project rejection looks like this in the agent's view: +The fix is always the same: generate a per-project token and replace `ZCP_API_KEY` on the surface ZCP reads from. In remote setup, that value is injected into the `zcp` service environment. In local setup, it is the `env` block in `.mcp.json`. -```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 the MCP. +## Where the token lives - remote vs local {#where-the-token-lives--remote-vs-local} -## 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 | +| Setup | 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` | +| Remote setup | The `zcp` service container env | Zerops platform - injected automatically when the service boots | +| [Local setup](/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 remote setup, the service starts with `ZCP_API_KEY` populated. If **Include Coding Agent** is enabled, the bundled agent uses that environment automatically. -In the local bridge, `zcp init` writes a token-less `.mcp.json` template; you add the `env` block: +In local setup, `zcp init` writes a token-less `.mcp.json` template; you add the `env` block: ```json { @@ -74,30 +67,20 @@ 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 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. | +| `ZCP_API_KEY` | ZCP itself, against the Zerops API | Container env (remote) or `.mcp.json` env block (local) | +| `GIT_TOKEN` | A git push from remote setup to a remote (GitHub, GitLab) | Remote: secret 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 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: - -```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 / -``` +`GIT_TOKEN` only matters when delivery uses git-push and remote setup is the one pushing. In local setup, 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 `zcp` service env when you run git-push setup, ZCP reuses it instead of asking for a new credential. -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. +`ZEROPS_TOKEN` is a **Zerops API token, not a GitHub token** — an 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. Delivery setup should place it in the CI secret store for the repository that runs the deploy. ## Rotation Rotate in the Zerops dashboard, then propagate to the consuming surface: -- **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. +- **Remote setup** — the service env value updates; ZCP picks up the new value the next time the `zcp` service boots or restarts. +- **Local setup** — paste the new token into the `env` block of `.mcp.json`, then restart your agent client 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. @@ -108,40 +91,40 @@ 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. 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. | +| **Service deletion** — removes the service entirely; container, deployed code, env vars, and (for managed services) data go with it. | Requires explicit approval in the same conversation, including the service name. Remote setup also blocks deleting the `zcp` service it is running in. | +| **Wholesale service replacement** from an import on a service with prior failed deploy history | First returns a summary of what would be replaced and refuses to continue. The agent must show that evidence before asking you to confirm. | -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. +ZCP itself adds confirmation only for the two operations above. Your agent client or team policy may still prompt before tool calls. Deploys, env changes, lifecycle actions, restarts, scaling, and public-access changes do not get an additional ZCP-specific confirmation gate. Most are reversible by another call. A few are operationally destructive even though they are not gated by ZCP (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 — 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. +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. +- **Replace-as-reset** — replacing a service from an import 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 the MCP refuses outright, see [Trust model](/zcp/security/trust-model). +Threat model and hard refusals: [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 the MCP boot at all. Generate a per-project token before connecting. +- **Multi-project tokens are refused at startup, not at first deploy.** An account-wide 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 the workspace performs the push); `ZCP_API_KEY` authorizes the MCP against Zerops. Mixing them grants too much or fails authentication. +- **`GIT_TOKEN` is not `ZCP_API_KEY`.** `GIT_TOKEN` authorizes git push in remote setup; `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 MCP 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 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). +- **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), and review service-scoped events, logs, deploy results, and git history before approving destructive recovery. ## 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. +- [Set up local ZCP](/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 index c4f059689..6a3992004 100644 --- a/apps/docs/content/zcp/security/trust-model.mdx +++ b/apps/docs/content/zcp/security/trust-model.mdx @@ -1,97 +1,76 @@ --- title: "Trust model" -description: "ZCP MCP gives a coding agent project-scoped power inside one Zerops project. The hosted and local paths set different blast radii." +description: "ZCP gives a coding agent operational access scoped to one Zerops project. Remote and local setup set different blast radii." --- -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 - -```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** — 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. +The security model starts with one rule: a ZCP process operates one Zerops project. Remote and local setup share that project boundary, but they expose different surroundings to the agent. + +## Remote setup or local setup + +**Remote setup** is the project-contained path. + +- ZCP runs in a `zcp@1` service inside the Zerops project. +- When **Include Coding Agent** is enabled, the bundled agent CLI runs there and is preconfigured to use ZCP. +- It uses a project-scoped token and reaches the project's private network. +- Runtime files are visible only when they are mounted into remote setup. +- Safety profile: **project-contained by design** - no direct access to your laptop, home directory, or other projects. + +**Local setup** is the supervise-the-client path. + +- The agent runs on your laptop. +- It can touch the same project surface, **plus whatever the agent client can reach on your laptop**. +- It reaches project services over VPN, plus everything else your laptop can already reach. +- Safety profile: **supervise the agent client** - ZCP stays project-scoped, but the agent process inherits your user. + +Either path is the right choice in context. Local setup 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 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. +One ZCP process works against one Zerops project. The boundary starts at authentication: at startup, ZCP 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 token form is 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, the MCP exposes those operations. If Zerops rejects the token for an operation, the MCP 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, ZCP exposes those operations. If Zerops rejects the token for an operation, ZCP can't bypass it. | Question | Boundary | |---|---| -| What project can the MCP see? | The one project resolved from the token at startup. | +| 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 +## Remote setup specifics -The hosted workspace is the safe-by-design path. A few specifics: +Remote setup is the project-contained path. A few specifics: - **`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. +- **Project access stays inside the project.** The `zcp@1` service can read project env and reach project services so the agent can build against real dependencies without you copying credentials around. +- **Filesystem reach is narrower than network reach.** The agent reaches project services over the private network, but only sees runtime files when they are mounted into remote setup. +- **A terminal in remote setup has the same project-scoped access as the agent.** Convenient, and the reason production stays in a separate project. -## Local agent bridge specifics +## Local setup specifics -The local bridge is the supervise-the-client path. A few specifics: +Local setup 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; 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. +- **VPN setup is outside ZCP's authority.** Bringing up the Zerops VPN requires admin or root approval on your laptop. ZCP can tell 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. +- **Local setup 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). +Full setup: [Set up local ZCP](/zcp/setup/local-agent-bridge). -## What the MCP refuses by design +## Audit surface -The MCP uses hard refusals for boundaries that should never be inferred, and confirmation gates for actions that are destructive but sometimes necessary. +Zerops records platform-side evidence: service events, deploy/build results, runtime logs, lifecycle actions, scaling, and public-access changes. It does not record the agent's private reasoning, every shell edit, browser-helper actions, or prompt history outside your agent client. -| Behavior | When it triggers | Result | -|---|---|---| -| 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. | +When taking over from an agent, read evidence in this order: service-scoped events, runtime logs, deploy/verify result, then git history when delivery uses git-push. -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. +## What ZCP refuses by design -Detailed confirmation flow and the diagnose-first rule: [Tokens and credentials → Confirmation gates](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions). +ZCP refuses boundaries that should never be inferred: tokens that do not resolve to exactly one project, remote self-deletion, and destructive replacement before the agent has read the relevant failure evidence. Destructive confirmation details live in [Tokens and credentials](/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. +- [Troubleshooting](/zcp/reference/troubleshooting) — recover when the agent or session state drifts. +- [Production boundary](/zcp/security/production-policy) — keep production outside the agent loop. diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index 726ae24f6..ed2d4575a 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -1,102 +1,50 @@ --- -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." +title: "Choose remote or local setup" +description: "Choose remote setup inside Zerops or local setup in your own editor/CLI." --- -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. +Choose where the ZCP-backed agent runs. ZCP gives the agent the same project-scoped Zerops toolset in both paths, but the filesystem, network path, deploy source, and safety profile are different. -:::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. -::: +Use remote setup for the first run and project-contained isolation. Use local setup when your checkout, editor, dev server, and git credentials should stay on your machine. -## The short rule +## 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. +**Remote setup** is the default first run: no local install, project-contained isolation, optional bundled agent, optional browser IDE. -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). +**Local setup** fits when you want to keep your editor, shell, checkout, local dev server, and git credentials. -## Hosted workspace +## Compare the paths -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. +**Remote setup** -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. +- ZCP runs inside the Zerops project in a `zcp@1` service. +- The agent runs inside that service when **Include Coding Agent** is enabled. +- Runtime files can be mounted through SSHFS. +- Managed services are reached over the project-private network. +- Deploy source, env vars, and git credentials live inside the project service. +- Safety profile: project-contained by design. -**Pick this when** +**Local setup** -- 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. +- ZCP runs on your laptop as the `zcp` binary. +- The agent runs in your local editor or CLI after `zcp init`. +- App work happens in your local working directory. +- Managed services are reached from your app and shell through Zerops VPN. +- Deploy source and git credentials are your local working directory and local git setup. +- Safety profile: ZCP is project-scoped, but the agent client runs as your user. -**What it gets you on top of the MCP** +## What changes in practice -- 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. +In remote setup, a `zcp@1` service runs the `zcp` binary inside the Zerops project. Enable **Include Coding Agent** when you want the service to add the bundled agent CLI and preconfigure it to use ZCP. Enable **Cloud IDE** when you also want browser-based VS Code. Runtime files can be mounted into the service, and managed services are reachable over the project-private network. -**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. +In local setup, the agent works in your local checkout. You install the `zcp` binary and run `zcp init` in the project folder so the local agent can talk to ZCP. ZCP talks to the Zerops API directly, while your local app and shell need `zcli vpn up` to reach managed services by hostname. -Setup: [Provision a hosted workspace](/zcp/setup/hosted-workspace). +After setup, ZCP reads whether the project has one runtime, a dev+stage pair, or a local checkout linked to a runtime. You usually state the outcome, not the label: dev+stage, one dev runtime, or local deploys to a named runtime. Exact labels live in [Workflow terms](/zcp/reference/agent-workflow#runtime-layouts). -## 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 the MCP 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. +Stage is a review or promotion target, not production. Production should live in a separate Zerops project; see [Production boundary](/zcp/security/production-policy). ## 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. +- [Set up remote ZCP](/zcp/setup/hosted-workspace) +- [Set up local ZCP](/zcp/setup/local-agent-bridge) +- [Build with ZCP](/zcp/workflows/build-with-zcp) diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index 057eb0ebe..900604143 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -1,36 +1,38 @@ --- -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." +title: "Set up remote ZCP" +description: "Add a zcp@1 service to a Zerops project and choose whether it includes a coding agent, Cloud IDE, or both." --- -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. +Remote setup is a `zcp@1` service inside your Zerops project. It keeps ZCP, project-scoped credentials, private-network access, and optional agent tooling inside the project boundary. -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). +Enable **Include Coding Agent** when you want Zerops to add the bundled agent CLI and preconfigure it to use ZCP in that service. Enable **Cloud IDE** when you also want browser-based VS Code for watching or intervening. -For the broader local-dev landscape (humans, not just agents), see [Local & Remote Development](/features/local-remote-development). +After provisioning, the service is ready for your first app instruction. If **Cloud IDE** is enabled, you can open the browser workspace from the dashboard; otherwise use the access method configured for the service. -## Two paths to the same workspace +Remote and local setup have the same ZCP project boundary but different work surfaces. The comparison lives in [Choose remote or local setup](/zcp/setup/choose-workspace); the broader human development modes live in [Local & Remote Development](/features/local-remote-development). -Zerops provisions the workspace for you — no manual install, no YAML to paste. Pick the path that matches what you're starting from: +## Two paths to remote setup -- **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. +Zerops provisions the service for you - no local install, no MCP config to write by hand, no YAML to paste. Pick the path that matches what you're starting from: -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 - 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 a `zcp@1` service with **Include Coding Agent** enabled. +- **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. -## Path A — Recipe + AI Agent environment +Both paths produce the same base result: a `zcp@1` service inside the project with `ZCP_API_KEY` injected automatically and scoped to this project. **Include Coding Agent** adds the preconfigured agent. **Cloud IDE** adds the browser editor. -Use this when you want a working stack quickly and the recipe matches the runtime shape you need. +## Path A — recipe + AI Agent environment + +Recipes are the quickest path when their stack matches the runtime layout 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. +The recipe ships its full service set; the **AI Agent** environment is what adds the `zcp@1` service with **Include Coding Agent** enabled 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. +If you pick a non-AI environment (Remote CDE, Local, Stage, Small Production, Highly-available Production) the recipe still deploys, but no `zcp` service with a bundled agent is added. Add ZCP afterward via Path B. -## Path B — Toggle the ZCP service +## Path B — toggle the ZCP service Use this for a custom project with no recipe, or when you already have a Zerops project running. @@ -39,56 +41,61 @@ 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. +4. The **ZCP CONFIGURATION** panel appears. Keep **Include Coding Agent** enabled to add and preconfigure the bundled agent inside the service. Use **Configure** when you need to choose authentication or adjust Cloud IDE access; otherwise leave the defaults 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 +## Open the service -The `zcp` service hosts a browser-based code editor (code-server) with Claude Code preconfigured in the terminal. +Open the `zcp` service from the Zerops dashboard after it reaches running state. 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. +3. If **Cloud IDE** is enabled, open the service workspace URL. Code-server launches in a new tab. If the service is exposed on a `.zerops.app` subdomain, see [Public access](/features/access) for the routing model. + +If **Cloud IDE** is disabled, remote setup can still exist as a project-contained service, but there is no browser editor to open. Use the access method configured for the service, such as SSH, or enable Cloud IDE when you want a browser workspace. -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. +## Run the agent -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). +When **Include Coding Agent** is enabled, the bundled agent starts already connected to this project through ZCP. In the Cloud IDE path, the terminal has the bundled agent available with `ZCP_API_KEY` in its environment. -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. +For a connection sanity check, ask the bundled agent: -## Verify the connection +```text +List the services in this project and tell me which are runtimes versus managed dependencies. +``` -When the editor opens, the terminal already has Claude Code running, scoped to this project, with `ZCP_API_KEY` in its environment. +A working setup answers with the runtime services (for example `appdev`, `appstage`) and managed services (for example `db`), and says whether the project has one runtime or a dev+stage pair. -Ask Claude Code to list the project services: +For real work, ask for the app behavior, not the Zerops procedure: ```text -List the services in this project and tell me which are runtimes versus managed dependencies. +Build a task board where tasks stay saved after refresh. ``` -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).) +Discovery, service setup, deploy, verification, and final reporting happen behind that app request. + +If **Include Coding Agent** is disabled, the `zcp@1` service exists but no preconfigured agent is waiting inside it. Enable the option or use [local ZCP](/zcp/setup/local-agent-bridge) from your own agent client. + +After the first setup pass, runtime services can be SSHFS-mounted into the service filesystem with one folder per runtime. 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 remote setup. -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. +If you added ZCP to an existing project, the agent should inspect existing services and use them instead of creating duplicates. That is handled as part of the first product prompt; the details are explained in [Service setup behind the prompt](/zcp/workflows/create-or-adopt-services). -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. +When Cloud IDE is enabled, the browser workspace is persistent. Close the browser tab and reopen the workspace link later; editor state, open files, and the agent session survive across reconnects as long as the `zcp` service is running. -## Hack on the workspace +## If connection fails -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. +If the agent can't reach ZCP, the service may still be starting. Wait for it to reach running state in the dashboard and reload the workspace. If **Include Coding Agent** was not enabled, add it or use local ZCP from your own agent client. -Common patterns: +At this point, the agent can receive the first app instruction. The [Quickstart](/zcp/quickstart) shows the shortest end-to-end run. -- **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. +## Customize remote setup -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. +Remote setup is a normal Zerops service, so teams can customize it like one: add tools, preload team config, or run helper processes next to the agent and Cloud IDE. Keep this separate from app runtime work; deploy app code to runtime services, not to `zcp`. ## Gotchas -- **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. +- **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. If the agent targets `zcp` as the app runtime, stop and correct the target. +- **Don't set `ZCP_API_KEY` by hand in remote setup.** It's platform-injected. Manual override risks breaking the agent's access. (Local setup 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 the bundled agent. 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 a7a4187d1..29a984043 100644 --- a/apps/docs/content/zcp/setup/local-agent-bridge.mdx +++ b/apps/docs/content/zcp/setup/local-agent-bridge.mdx @@ -1,34 +1,32 @@ --- -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." +title: "Set up local ZCP" +description: "Install the zcp binary, run zcp init in your project, and let a local coding agent operate one Zerops project." --- -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. +Local setup runs the `zcp` binary on your laptop, initialized in one project directory. The agent edits your checkout, your tools run the dev server, and ZCP deploys from your working directory to the linked Zerops runtime. -:::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. Expect setup details to shift between releases. -::: +`zcp init` writes the local MCP configuration for that folder, so a compatible local agent client can talk to ZCP as the `zerops` MCP server. With `zcli vpn up `, your laptop also joins the project's private network - the same network your services use. -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. +Local setup is newer than remote setup. Expect install details and generated project files to move between releases. -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. +Use this path when you already have a comfortable local setup - your editor, your shell, your dev server - and want a coding agent that can drive Zerops from the same machine. If you're evaluating ZCP for the first time, [remote setup](/zcp/setup/hosted-workspace) is a faster start. -:::caution Supervise the agent client -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). -::: +You don't need ZCP at all to develop locally against a Zerops project: `zcli vpn up` plus your editor is enough. ZCP is what you add when you also want a coding agent operating the project from the same machine: discovering services, generating local env snapshots, deploying, reading logs, verifying. + +Security note: ZCP itself stays project-scoped, but the agent client runs as your local user. Set your client permissions accordingly. ## 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; the MCP doesn't see your subscription credentials. +- A compatible local agent client installed and logged in. ZCP doesn't see the agent client's subscription or login credentials. ## Get a project-scoped Zerops token -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). +ZCP needs a Zerops API token scoped to **one project**. Multi-project tokens, including account-wide full-access tokens, are refused at startup. Generate one in the Zerops dashboard, then keep the value handy for the local MCP configuration step. Full walkthrough, rejection rules, and rotation: [Tokens and credentials](/zcp/security/tokens-and-project-access). -## Install the binary +## Install `zcp` ```bash curl -sSfL https://raw.githubusercontent.com/zeropsio/zcp/main/install.sh | sh @@ -36,7 +34,7 @@ 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 +## Run `zcp init` in your project folder In a terminal in your project's working directory: @@ -44,20 +42,13 @@ In a terminal in your project's working directory: 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. +`zcp init` writes the local MCP configuration and agent instructions for this project. Re-running it may refresh generated config; after rerunning it, confirm `.mcp.json` still contains `ZCP_API_KEY` before launching the agent. -## Add your token +## Configure `zcp` as the local MCP server -`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). +`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 your agent client from the 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--remote-vs-local). -Claude Code starts and lists the MCP servers it found. The `zerops` server (that's ZCP MCP) should be in the list. +The agent client starts and lists the MCP servers it found. The `zerops` server should be in the list; that is the local ZCP connection. ### Sanity check @@ -65,59 +56,55 @@ 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 the MCP, 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 ZCP, see [Troubleshooting](/zcp/reference/troubleshooting) — most common issues are token-scope mismatches and starting the agent client from the wrong directory. -## Connect the project VPN +## Connect private services with `zcli vpn up` -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: +Your laptop reaches managed services (databases, caches, storage) through the Zerops project [VPN](/references/networking/vpn). ZCP 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. The MCP 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. 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` +## Local env bridge -Ask Claude Code: +When a local app needs project credentials, ask for the product change and let ZCP generate the local env bridge as part of that work. For a standalone sanity check, you can ask: ```text Use ZCP to generate a .env file for my local app. ``` -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. - -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. +ZCP resolves the project env references your app would see when deployed and writes a `.env` snapshot in your working directory. Your local app uses the same hostnames and credentials, reached over VPN. Regenerate after changing project env variables. -## Link a deploy target +## Linked 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, the MCP picks it up automatically. If multiple, the agent asks which to link — answer by hostname (e.g. `appstage`). +Local setup needs one Zerops runtime linked as the deploy target, often a stage runtime, 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. +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. 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). +With a linked runtime, ZCP can deploy from your working directory. Without one, it can still inspect the project and generate env snapshots, but deploys need a target runtime first. ## What stays your tool's job -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 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 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. +ZCP doesn't mount Zerops runtime filesystems on your laptop. Remote ZCP can give the agent SSHFS access; local ZCP 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 MCP 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; the agent client 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; the MCP and Claude Code both expect it. +- **The `zerops` server name in `.mcp.json` is fixed.** Don't rename the key; ZCP and the agent client 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). +- **Production stays out of this loop.** Local ZCP is for development and staging projects. ## Next steps -- [Build and verify an app](/zcp/workflows/build-and-verify-app) — the deploy and verify loop with the agent. +- [Deploy, verify, and fix](/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. +- [Choose remote or local setup](/zcp/setup/choose-workspace) - remote vs local and runtime layouts. 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 15364737e..a074f62b8 100644 --- a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx +++ b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx @@ -1,101 +1,67 @@ --- -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." +title: "Deploy, verify, and fix" +description: "How the agent uses ZCP to edit code, deploy through Zerops, read evidence, fix failures, and prove 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. +After the target runtime and dependencies are known, the agent can change code and `zerops.yaml`, deploy the runtime, fix failures from evidence, and verify the result. -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. +A ZCP task is done only when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the working URL or a concrete blocker. -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. +## What the agent does -## Start the work +ZCP treats deploy as diagnostics and verification as the completion gate. The user does not have to script this sequence; it is the expected path behind an app prompt. -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). +- **Runtime scope.** The agent identifies which runtime changed. +- **Code and `zerops.yaml`.** The agent changes app files and runtime config where needed. +- **First deploy.** The first verified runtime deploy goes through ZCP. +- **Runtime reachability.** The agent checks that the runtime answers on the real platform path. +- **Requested behavior.** The agent proves the endpoint, URL, UI state, row, job result, or other acceptance evidence. +- **Evidence-based fix.** If either verification layer fails, the agent reads new logs/events/check output, fixes the cause, and redeploys. -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. +A green build or running runtime is not done until the requested endpoint, UI, or state is checked on the real URL. Retries should use new evidence, not repeat the same deploy. -Good starting prompts: +## Runtime scope is a precondition -```text -Add a /health endpoint to appdev and verify it returns 200. -``` +Before editing, the agent should make the runtime service visible: `appdev`, `appstage`, `app`, or the linked runtime in a local setup. Managed services such as databases and caches are dependencies, not deploy targets. -```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. +If the agent starts writing files before it knows the runtime scope, treat that as a correction signal: it should read ZCP status and identify the target before continuing. ## 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, 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. +The first deploy uses the direct ZCP deploy path. Delivery choices such as git-push or external handoff apply after a verified deploy exists. -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. +Deploy failures point to different evidence: -## Read logs and events - -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. -- 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." +| Failure point | Read first | +|---|---| +| Build failed | Build logs and `zerops.yaml` build steps | +| Runtime started then crashed | Runtime logs and start command | +| App is up but route fails | Runtime logs at request time and behavior check output | +| Local app cannot reach managed services | VPN state and generated `.env` values | ## Verify -After a deploy succeeds, verification runs in two layers: +Verification has 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 | +| Layer | What it proves | |---|---| -| 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. | +| Platform reachability | The service is running, recent error logs are checked, and HTTP runtime services answer a probe when eligible. | +| Requested behavior | The thing you asked for works: endpoint body, UI state, database row, job result, or stage URL. | -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. +Both layers must pass before the agent reports completion. -If the agent gets stuck before the loop closes: +## Fix or stop -- **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). +The agent should name the failure point in plain language, read fresh logs/events/check output, fix the indicated cause, and redeploy. A repeated retry with no new evidence is not progress. -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. +Stop and ask for a decision when scope is ambiguous, credentials are missing, destructive recovery is needed, or several evidence-based retries hit the same failure. ## 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. +The platform records deploys, events, runtime logs, and lifecycle actions. It does not record every agent thought, browser click, or shell edit. When taking over, ask for ZCP status first, then read service-scoped events, logs, deploy/verify results, and git history if delivery uses git-push. ## 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 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 - -- [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. +1. **Deploy success is not verify success.** A green build with a broken feature is not complete. +2. **Stage is explicit.** In dev+stage projects, the agent should say when stage is included before changing or verifying it. +3. **Secrets belong in env vars.** If the app needs a third-party API key, the agent should ask for an env-var path, not put secrets in source. diff --git a/apps/docs/content/zcp/workflows/build-with-zcp.mdx b/apps/docs/content/zcp/workflows/build-with-zcp.mdx new file mode 100644 index 000000000..326d0ac07 --- /dev/null +++ b/apps/docs/content/zcp/workflows/build-with-zcp.mdx @@ -0,0 +1,44 @@ +--- +title: "Build with ZCP" +description: "How an outcome prompt becomes service setup, code, deploy, verification, fixes, and delivery." +--- + +Start with what the app should do, not with tool calls or workflow names. + +```text +Build a task board where tasks stay saved after refresh. +``` + +Behind that prompt, the agent should inspect the project, choose a target runtime, make the app change, and use real deploy evidence until the requested behavior is proven. ZCP supplies the project knowledge and tool access needed for that loop: live services and env state, Zerops guidance, deploys, logs, verification, and guarded infrastructure changes. + +Do not make the prompt longer to restate completion expectations such as deploy, verification, or returning the URL. The words you add should change the app, the stack, the runtime layout, the acceptance criteria, or the delivery path. + +You also do not need to paste an infrastructure inventory, env wiring plan, or log summary into the prompt. ZCP makes those available to the agent from the live project. + +## What happens after your prompt + +The agent should make this path visible as it works: + +**Project discovery.** Read what exists now: runtime services, managed services, env vars, logs, recent events, and current work state. + +**Service setup.** Use existing services when they fit, or create the missing runtime and managed dependencies before app code starts. + +**Runtime scope.** Identify the app runtime that will receive code, such as `appdev`, `appstage`, `app`, or a linked local deploy target. The `zcp` service is never the app target. + +**Code, deploy, verify, fix.** Change app files and `zerops.yaml`, deploy through Zerops, verify reachability and requested behavior, read evidence on failure, and retry from the cause. + +**Delivery after proof.** After a verified deploy exists, keep direct deploy, push to git, or use external handoff to CI or a human. + +Advanced export/reuse path: [Package a running service](/zcp/workflows/package-running-service). + +## What the final answer should contain + +For a completed app task, the agent should report: + +- which runtime service changed, +- which deploy passed, +- which URL, endpoint, or UI state proved the requested behavior, +- any env vars, managed services, or delivery settings touched, +- the delivery choice if one was set. + +If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the next decision needed from you. diff --git a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx index 7917e966a..e1dc9125b 100644 --- a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx +++ b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx @@ -1,40 +1,39 @@ --- -title: "Create or adopt services" -description: "Use ZCP to create new Zerops services or adopt existing ones before any application work starts." +title: "Service setup behind the prompt" +description: "How ZCP helps the agent use existing app services or create missing runtimes and managed dependencies before code 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. +When an app prompt needs infrastructure, ZCP should discover what already exists before creating runtimes or managed services. This usually happens because you asked for a product outcome, not because you explicitly asked for infrastructure. -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. +For example, `Build a task board where tasks stay saved after refresh.` may require an app runtime and database. `Use the existing Laravel services` tells the agent to reuse what is already there instead of creating duplicates. -## What this workflow does +Behind that prompt, the agent should answer: -Create-or-adopt is **infrastructure only**. It answers: - -- Which runtime services will hold application code? +- Which runtime service will hold app 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). +- Do those services already exist, or should the agent create them? +- Are they running and visible to ZCP? -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. +This part does **not** write application code, create `zerops.yaml`, run the first deploy, or verify behavior. Those belong to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). -## Two starting situations +## Starting situations -| Situation | What the agent should do | +| Situation | What should happen | |---|---| -| **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. | +| Runtime services already exist | Use them. Identify runtimes and managed dependencies. Do not recreate or rename services just to begin. | +| Project has only the `zcp` service | Treat it as empty from the app point of view. Create or import the requested service layout. | +| Project is empty and request matches a known stack | A recipe may be a good starting base, but the starter still has to be changed into the requested app. | +| Project is empty and request needs a custom layout | Create a custom service plan before app work starts. | +| Setup was interrupted | Resume from current ZCP status instead of starting over. | -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. +The `zcp` service is the ZCP setup, not the application runtime target. -## Prompts that work +## Prompt examples -Write the prompt in terms of the project shape you want. Name a runtime hostname only when you already know it. +Product prompts can imply the service setup; they do not need to spell it out: ```text -Create the services for a Node.js API with PostgreSQL. Use appdev and appstage. +Build a Node.js API with PostgreSQL. Use the existing dev+stage pair if this project has one. ``` ```text @@ -42,39 +41,22 @@ 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. +Add saved tasks to this task board. ``` -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: +The agent should explain whether it used existing services or created new ones, then move into app work when infrastructure is known. -- 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. +## Done state -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). +Service setup is done when the app runtime and managed dependencies are known, any new services are visible, and the agent is ready to start app code, `zerops.yaml`, deploy, and verification work. -## Common failures +When files, framework setup, deploys, logs, or behavior checks appear, the work has moved to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). -| 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. | +## Failure signals -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. +| Symptom | What to do | +|---|---| +| Agent targets `zcp` as the app | Correct the target to the runtime service. | +| Agent recreates existing runtimes | Ask it to inspect current services and use what exists. | +| Agent writes app files during service setup | Stop and finish infrastructure first, or move explicitly to app work. | +| Service creation fails | Treat it as a hard stop until the failure is understood. | diff --git a/apps/docs/content/zcp/workflows/delivery-handoff.mdx b/apps/docs/content/zcp/workflows/delivery-handoff.mdx index d4df0e798..6bdfe09a6 100644 --- a/apps/docs/content/zcp/workflows/delivery-handoff.mdx +++ b/apps/docs/content/zcp/workflows/delivery-handoff.mdx @@ -1,85 +1,49 @@ --- title: "Choose how finished work ships" -description: "Choose whether the agent closes work by deploying directly through the MCP, pushing to git, or handing off to your CI." +description: "After a verified deploy, choose whether future changes use direct deploy, git-push, or an external handoff." --- -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. +Delivery is decided after the app works. You can include delivery intent in the original product prompt, but ZCP applies it only after a verified deploy exists. The first deploy uses ZCP's direct deploy path; the delivery choice records how future changes should be delivered. -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. +## Three delivery choices -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. +**Keep direct deploy** for fast iteration, solo projects, and demos. The agent keeps deploying through ZCP directly. -## Three answers +**Push to git** when you want commits, reviews, or a repository-driven build. The agent commits and pushes to a configured remote; a build integration may run afterward. -- **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.** 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. +**External handoff** when your CI, release process, or a human owns delivery. ZCP records what happened but does not initiate future deploys. -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. +You can ask in plain language: ```text -Set up git-push delivery for appdev. Push to git@github.com:my-org/notes-app.git. +After the app verifies, set up git-push delivery to git@github.com:my-org/task-dashboard.git. ``` ```text -Keep ZCP deploying appdev directly for now. +Keep ZCP deploying directly for now. ``` ```text -ZCP records changes for appdev, but my CI takes over from here. +My CI takes over after this verified deploy. ``` -## 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 to git -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). +Git-push needs committed code, a remote URL, and credentials that can push. -### What fires after a push lands +Remote and local setup differ: -- **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). +- Remote setup uses credentials configured inside the `zcp` service, commonly a fine-grained git token. +- Local setup uses your local git credentials and working directory. -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 - -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. - -In this mode, ending the session is a deliberate step — the agent doesn't auto-close even after a clean deploy and verify. +A push is not proof that the app works. If a webhook or GitHub Actions build runs from the push, the agent should observe the build result and verify the app afterward. ## 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. +Delivery choice is not permanent. You can move from direct deploy to git-push later, add a build integration later, or switch to external handoff for a release that needs human 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. +1. **Delivery choice is not a hidden deploy button.** It records the future path after the app already works. +2. **External handoff is not a deploy by itself.** It means a human or external system owns delivery. +3. **GitHub Actions uses a Zerops token.** The Actions secret for `zcli` deploys is `ZEROPS_TOKEN`, not your GitHub token. diff --git a/apps/docs/content/zcp/workflows/package-running-service.mdx b/apps/docs/content/zcp/workflows/package-running-service.mdx index 476485917..2e1992218 100644 --- a/apps/docs/content/zcp/workflows/package-running-service.mdx +++ b/apps/docs/content/zcp/workflows/package-running-service.mdx @@ -3,9 +3,9 @@ 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. +Use packaging when a deployed runtime should become a reusable import bundle. This is an advanced handoff or reuse workflow, not the normal way to deploy the next change. 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. +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 layout into a fresh Zerops project. ## When to package @@ -17,10 +17,10 @@ Packaging is the right tool when: 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. +- You want the next app change deployed. For that, ask for the app result; direct deploy is the expected ZCP path behind the request. +- You want to deploy the next change to an existing production project. 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. +Packaging does not keep a long-running session. If interrupted, the agent reads live state and re-runs the export flow; you only decide when the dev or stage runtime, or env classification, is ambiguous. ## Pick the runtime to package @@ -38,7 +38,7 @@ The agent handles the call sequence; you only step in if it asks which half to p ## 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: +Once the runtime is chosen, 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 | |---|---|---| @@ -47,7 +47,7 @@ Once the runtime and variant are picked, ZCP composes a candidate bundle and pau | `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. +ZCP does not guess these buckets from key names alone. The agent should classify them from source code: if the value is read by a Stripe SDK call it is `external-secret`; if it appears as `${db_*}` from a managed service it is `infrastructure`. The packaging response carries env *keys* and redacts values; the agent fetches values only 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: @@ -64,7 +64,7 @@ Auto-secret rotation is destructive to any persisted state encrypted with the ol A successful run produces a **single-repo, self-contained bundle** with two files: ```yaml -#yamlPreprocessor=on +#zeropsPreprocessor=on # zerops-project-import.yaml — paste into a fresh Zerops project project: name: demo @@ -113,13 +113,13 @@ zerops: 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 first line is the [Zerops preprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `<@...>` directives (here `<@generateRandomString(<32>)>` for `APP_KEY`), `#zeropsPreprocessor=on` must be 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. +Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen handoff path expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through the normal [handoff path](/zcp/workflows/delivery-handoff). -## What the bundle does NOT include +## What the bundle does not include | Not in the bundle | Where it lives instead | |---|---| @@ -130,12 +130,12 @@ Once the bundle comes back, ask the agent to write the two files into your repo ## 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. +1. **Packaging is not the delivery path by itself.** It produces the bundle. Commit and push happen through your chosen [handoff path](/zcp/workflows/delivery-handoff). 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. +- [Deploy, verify, and fix](/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/sidebars.js b/apps/docs/sidebars.js index 2910d12ef..2d771d4a6 100644 --- a/apps/docs/sidebars.js +++ b/apps/docs/sidebars.js @@ -152,7 +152,7 @@ module.exports = { { type: 'doc', id: 'features/coding-agents', - label: 'Infrastructure for Coding Agents', + label: 'ZCP for Coding Agents', customProps: { sidebar_icon: 'sparkles', }, @@ -579,7 +579,7 @@ module.exports = { }, { type: 'category', - label: 'Zerops Control Plane MCP', + label: 'Zerops Control Plane', collapsible: false, customProps: { sidebar_is_group_headline: true, @@ -614,7 +614,7 @@ module.exports = { }, { type: 'category', - label: 'Connect', + label: 'Choose remote or local setup', link: { type: 'doc', id: 'zcp/setup/choose-workspace', @@ -627,26 +627,21 @@ module.exports = { { type: 'doc', id: 'zcp/setup/hosted-workspace', - label: 'Provision a hosted workspace', + label: 'Set up remote ZCP', }, { 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', + label: 'Set up local ZCP', }, ], }, { type: 'category', - label: 'Build & ship', + label: 'Build with ZCP', link: { type: 'doc', - id: 'zcp/workflows/build-and-verify-app', + id: 'zcp/workflows/build-with-zcp', }, customProps: { sidebar_icon: 'circle-stack', @@ -656,23 +651,23 @@ module.exports = { { type: 'doc', id: 'zcp/workflows/create-or-adopt-services', - label: 'Create or adopt services', + label: 'Service setup behind the prompt', }, { type: 'doc', - id: 'zcp/workflows/delivery-handoff', - label: 'Delivery handoff', + id: 'zcp/workflows/build-and-verify-app', + label: 'Deploy, verify, and fix', }, { type: 'doc', - id: 'zcp/workflows/package-running-service', - label: 'Package a running service', + id: 'zcp/workflows/delivery-handoff', + label: 'Choose how finished work ships', }, ], }, { type: 'category', - label: 'Trust & recover', + label: 'Security', link: { type: 'doc', id: 'zcp/security/trust-model', @@ -682,41 +677,57 @@ module.exports = { }, className: 'homepage-sidebar-item', items: [ + { + type: 'doc', + id: 'zcp/security/tokens-and-project-access', + label: 'Tokens and credentials', + }, { type: 'doc', id: 'zcp/security/production-policy', label: 'Production boundary', }, + ], + }, + { + type: 'category', + label: 'Reference', + link: { + type: 'doc', + id: 'zcp/reference/index', + }, + customProps: { + sidebar_icon: 'command-line', + }, + className: 'homepage-sidebar-item', + items: [ + { + type: 'doc', + id: 'zcp/reference/agent-workflow', + label: 'Workflow terms', + }, { type: 'doc', - id: 'zcp/security/auditing-agent-work', - label: 'Auditing agent work', + id: 'zcp/reference/mcp-operations', + label: 'Advanced operations', + }, + { + type: 'doc', + id: 'zcp/workflows/package-running-service', + label: 'Package a running service', }, { type: 'doc', id: 'zcp/reference/troubleshooting', label: 'Troubleshooting', }, + { + type: 'doc', + id: 'zcp/glossary', + label: 'Glossary', + }, ], }, - { - type: 'doc', - id: 'zcp/reference/agent-workflow', - label: 'Agent workflow contract', - customProps: { - sidebar_icon: 'command-line', - }, - className: 'homepage-sidebar-item', - }, - { - type: 'doc', - id: 'zcp/glossary', - label: 'Glossary', - customProps: { - sidebar_icon: 'book-open', - }, - className: 'homepage-sidebar-item', - }, ], }, { @@ -3534,4 +3545,4 @@ module.exports = { // }, // }, // ], -}; \ No newline at end of file +}; diff --git a/apps/docs/static/llms-full.txt b/apps/docs/static/llms-full.txt index 5bebd6c4a..9262f13f7 100644 --- a/apps/docs/static/llms-full.txt +++ b/apps/docs/static/llms-full.txt @@ -20,7 +20,7 @@ zerops: - curl something else # OPTIONAL. Build your application buildCommands: - - + - # REQUIRED. Select which files / folders to deploy after # the build has successfully finished deployFiles: app @@ -155,7 +155,7 @@ zerops: base: alpine@3.20 # OPTIONAL. Build your application buildCommands: - - + - ... ``` Build commands are optional. Zerops triggers each command in the defined order in a dedicated build container, running from the `/build/source` directory. @@ -334,19 +334,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to an Alpine service with hostname = "app" and port = 8080 from another service of the same project, simply use `app:8080`. Read more about [how to access an Alpine service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the Alpine runtime environment by installing additional dependencies or tools to the runtime base environment. The base Alpine environment contains {data.alpine.default}, Zerops command line tool and `git` and `wget`. To install additional packages or tools add one or more prepare commands: @@ -470,25 +465,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -510,16 +499,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Alpine application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -562,25 +547,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -603,16 +582,12 @@ Read more about how the [readiness check works](/alpine/how-to/deploy-process#re #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Alpine application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -637,7 +612,6 @@ Read more about how the [readiness check works](/alpine/how-to/deploy-process#re # Alpine > How To > Build Process - ## Build process overview Zerops starts a temporary build container and performs the following actions: 1. **Installs the build environment** - Sets up base system and runtime @@ -654,7 +628,7 @@ Configure your build process in your `zerops.yaml` file according to the full b The default build environment contains: - {data.alpine.default} - [zCLI](/references/cli), Zerops command line tool -- +- ### Customize build environment To install additional packages or tools, add one or more build.prepareCommands to your `zerops.yaml`. :::info @@ -662,23 +636,18 @@ The application code is available in the `/build/source` folder in your build co ::: ### Build hardware resources All runtime services use the same hardware resources for build containers: - HW resource Minimum Maximum - CPU cores 1 5 - RAM 8 GB 8 GB - Disk 1 GB 100 GB - Build containers start with minimum resources and scale vertically up to maximum capacity as needed. ### Build time limit The time limit for the whole build pipeline is **1 hour**. After 1 hour, Zerops will terminate the build pipeline and delete the build container. @@ -825,70 +794,39 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description - hostname - The unique service identifier. - The hostname of the new database will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Alpine service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -904,7 +842,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Alpine service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -965,7 +902,7 @@ It is also a great option when you need a specific version of a technology (like The default runtime environment contains: - {data.alpine.default} - [zCLI](/references/cli) -- +- ### When You Need a Custom Runtime Image Since Alpine serves as a general-purpose base, you'll likely want to customize it for your specific use case. Common scenarios include: :::important @@ -1567,25 +1504,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -1862,67 +1793,39 @@ The `project:` section is required. Only one project can be defined. | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Bun and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Bun service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -1938,7 +1841,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Bun service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -2046,7 +1948,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-bun), a **_recipe_**, containing the most simple Bun web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Bun app running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-bun/blob/main/zerops-project-import.yaml)): ```yaml @@ -2379,64 +2280,49 @@ All projects have a technical maximum backup storage limit of **1 TiB**. Usage b ::: ## Resource Pricing Services in Zerops require computing resources that are billed separately from your project core. These resources are allocated per service and billed by the minute based on actual usage, with credits deducted hourly. - Resource Price Description - Shared CPU $0.60 per CPU / 30 days Economical option for most workloads with good performance - Dedicated CPU $6.00 per CPU / 30 days Reserved CPU cores for predictable performance - RAM $0.75 per 0.25 GB / 30 days Memory allocated to your services - Disk Space $0.05 per 0.5 GB / 30 days Storage space for your applications and data - :::note Daily Spending Control You can set daily spending limits in GUI for your project to control costs and avoid unexpected charges. This provides an alternative to configuring automatic resource scaling ranges while keeping your services running optimally. ::: ## Additional Services Enhance your deployment with these optional services to meet specific requirements for networking, storage, and data transfer. - Service Price Description - Dedicated IPv4 $3.00 per 30 days Exclusive IPv4 address for your project (instead of shared) - Object Storage $0.01 per GB / 30 days Scalable storage for files, backups, and static assets - ## Overage Costs When you exceed the resources included in your project core plan, the following charges apply: - Item Price Description - Extra Egress $0.02 per GB Data transfer out of your project beyond plan limits - Extra Backup Space $0.50 per 5 GB Additional storage for automatic, encrypted backups - Extra Build Time $0.50 per 15 hours Additional time for building and deploying applications - ## Pricing Calculator Use our pricing calculator to estimate your monthly costs based on your specific needs: @@ -2948,25 +2834,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -3115,7 +2995,6 @@ Read more about how the [readiness check works](/deno/how-to/deploy-process#read # Deno > How To > Build Process - ## Build process overview Zerops starts a temporary build container and performs the following actions: 1. **Installs the build environment** - Sets up base system and Deno runtime @@ -3141,23 +3020,18 @@ The application code is available in the `/build/source` folder in your build co ::: ### Build hardware resources All runtime services use the same hardware resources for build containers: - HW resource Minimum Maximum - CPU cores 1 5 - RAM 8 GB 8 GB - Disk 1 GB 100 GB - Build containers start with minimum resources and scale vertically up to maximum capacity as needed. :::info Build container resources are not charged separately. Limited build time is included in your [project core plan](/company/pricing#project-core-plans), with additional build time available if needed. @@ -3321,68 +3195,40 @@ The `project:` section is required. Only one project can be defined. | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Deno and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Deno service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -3398,7 +3244,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Deno service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -3463,7 +3308,6 @@ If your Deno application needs more than what's included in the default environm - **Native dependencies**: When your Deno modules require system libraries that aren't in the default environment Here are Deno-specific examples of configuring custom runtime images in your `zerops.yml`: ### Basic Deno Setup - ### Using Build Files in Runtime Preparation ```yaml build: @@ -3549,7 +3393,6 @@ Have you got any additional question? Join our **[Discord](https://discord.com/i As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-deno), a **_recipe_**, containing the most simple Deno web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Deno app running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-deno/blob/main/zerops-project-import.yaml)): ```yaml @@ -3613,7 +3456,6 @@ Despite these limitations, Docker services offer some benefits: ## Configuration Guide ### Supported Version Currently supported Docker versions: -Import configuration version: ### Basic Structure Docker services in Zerops are configured through the `zerops.yaml` file. Here's a typical configuration pattern: ```yaml title="zerops.yaml" @@ -3625,7 +3467,7 @@ zerops: - docker image pull : # Always use specific version tags start: docker run --network=host : ports: - - port: + - port: httpSupport: true ``` :::important @@ -4176,19 +4018,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a .NET service with hostname = "app" and port = 5000 from another service of the same project, simply use `app:5000`. Read more about [how to access a .NET service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the .NET runtime environment by installing additional dependencies or tools to the runtime base environment. The base .NET environment contains {data.alpine.default}, the selected @@ -4317,25 +4154,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -4357,16 +4188,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your .NET application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -4409,25 +4236,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -4450,16 +4271,12 @@ Read more about how the [readiness check works](/dotnet/how-to/deploy-process#re #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your .NET application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -4605,85 +4422,50 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains .NET and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - The hostname of the new database will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. See what [.NET service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -4699,7 +4481,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add .NET service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -4807,7 +4588,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-dotnet-hello-world), a **_recipe_**, containing the most simple .NET web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of .NET running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-dotnet-hello-world/blob/main/import-project/description.yaml)): ```yaml @@ -5424,25 +5204,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -5719,68 +5493,40 @@ The `project:` section is required. Only one project can be defined. | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Elixir and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Elixir service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -5796,7 +5542,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Elixir service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -5904,7 +5649,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-elixir), a **_recipe_**, containing the most simple Elixir web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Elixir app running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-elixir/blob/main/zerops-project-import.yaml)): ```yaml @@ -5988,7 +5732,7 @@ See the [VPN reference guide](/references/networking/vpn). Connect securely to your project's internal network from your local machine: ```bash # Connect to your project -zcli vpn up +zcli vpn up # Access services using internal hostnames curl http://api:3000/health # Disconnect when done @@ -6037,7 +5781,6 @@ From this section, you can: - Create a one-time backup - Change the frequency/disable of automatic backups - Configure retention policies and limits - ### Backup Frequency Options Available schedules: - **No backups**: Disable automatic backups (not recommended) @@ -6046,25 +5789,18 @@ Available schedules: - **Once a month**: Monthly backups on a specific day and time - **Custom CRON**: Define a custom schedule using CRON syntax For the Custom CRON option, you can use the following syntax: - Field name Allowed values - Minute 0-59 - Hour 0-23 - Day 1-31 - Month 1-12 - Week Day 0–7; both 0 and 7 represent Sunday - Examples: - `0 2 * * *` - Every day at 2:00 AM - `0 4 * * 0` - Every Sunday at 4:00 AM @@ -6091,7 +5827,6 @@ In this section, you can: - View all backups with their timestamps and sizes - Download backups - Delete backups - :::note When creating manual backups via the UI, you'll see immediate feedback. If the backup takes longer than 10 seconds, the process continues in the background. You can verify completion by refreshing the backup list or checking service logs. ::: @@ -6412,35 +6147,27 @@ Zerops CDN is a global content delivery network that brings your static content - **Simple Integration**: No complex configuration required ## Global CDN Infrastructure Zerops CDN operates across **6 strategic regions** to ensure your content is always delivered from a location close to your users: - Region Location Coverage Area - EU CZ Prague, Czech Republic Primary European coverage + failover for all regions - DE Falkenstein, Germany - UK London, United Kingdom UK and surrounding areas - AU Sydney, Australia Australia and Oceania - SG Singapore, Singapore Southeast Asia - CA Beauharnois, Canada North America - ### Geo-Steering Technology Zerops CDN's geo-steering technology automatically routes users to the server location closest to them. Here's how it works: * **Automatic routing**: Users are directed to the optimal CDN node based on their geographic location @@ -6542,27 +6269,21 @@ Zerops provides multiple ways to manage and purge cached content before its norm --header "Authorization: Bearer $USER_OR_ACCESS_TOKEN" ``` #### Purge Pattern Examples - Pattern Description Example - `/*` Purges all content Useful after major updates - `/images/*` Purges all content in a directory Clear all cached images - `/css/main.css$` Purges a specific file Update a single CSS file - `/2023*` Purges content starting with pattern Clear content with date prefix - :::warning Pattern Rules - Wildcards (`*`) must be at the end of the pattern - Specific files must include `$` at the end @@ -6580,7 +6301,7 @@ The endpoint links below will take you to the Swagger documentation with detaile ### Cache Purge API - **[Purge Storage Mode Cache ↗](https://api.app-prg1.zerops.io/api/rest/public/swagger/#/PublicServiceStack/PurgeStorageCdn)** `PUT /api/rest/public/service-stack/{id}/purge-cdn/{path}` - **[Purge Static Mode Cache ↗](https://api.app-prg1.zerops.io/api/rest/public/swagger/#/PublicProject/PurgeStaticCdn)** `PUT /api/rest/public/project/{id}/purge-cdn/static/{domain}/{path}` -- **Purge Api Mode Cache *(Coming soon)*** +- **Purge Api Mode Cache *(Coming soon)*** ## Troubleshooting Having issues with your CDN? Here are solutions to the most common problems: #### Content Not Updated After Changes @@ -6619,6 +6340,67 @@ Remember that only publicly accessible objects will be cached by the CDN. Privat ---------------------------------------- +# Features > Coding Agents + +Coding agents need current project knowledge and a safe way to act on it. The hard part is rarely "write the function" — it's letting the agent connect product intent to infrastructure: services, env vars, logs, deploys, verification, and a path to production. +**Zerops Control Plane (ZCP)** gives a coding agent current project state and bounded operations on one Zerops project. You describe the app or change you want; ZCP lets the agent discover services, use existing infrastructure or create what is missing, wire the app to managed services, deploy through the standard pipeline, verify the real endpoint, inspect logs, and report a URL or blocker. You hold the product intent, constraints, approvals, and quality bar. +:::caution Keep production in a separate project +ZCP runs with a project-scoped token that grants operational rights inside that project. Put it in a development or staging project, not the project serving production traffic. That keeps agent-driven changes away from production unless your CI or release process promotes them. See [Production boundary](/zcp/security/production-policy). +::: +## Who it's for +Developers using a coding agent on application work that needs real infrastructure. The developer stays focused on the product outcome, technology choices, acceptance criteria, and judgment. ZCP gives the agent the platform context and operations needed to use or create services, connect the app to infrastructure, deploy, verify, recover from failure, and report back. +The **Include Coding Agent** option bundles the currently supported agent CLI shown in the dashboard. Local setup can connect a compatible local agent client after `zcp init`. +## What ZCP lets the agent do +ZCP exposes a fixed set of operations on one Zerops project, grouped by user job. The prompt can stay focused on the app because the agent does not need you to describe what the project looks like - ZCP reports it from what is 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 | [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) | +| **Verify** | Reachability + behavior checks against the actual URL | [Deploy, verify, and fix](/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) | +| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | +| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | +Behind one app request, the agent should read the project, use existing services or create missing ones, edit and deploy the app, verify the requested behavior, and then record how future changes should ship. A green deploy with a 500 on the relevant route is not done — verification has two layers and both must pass. +Full workflow terms: [Workflow terms](/zcp/reference/agent-workflow). +## Two ways to run it +The `zcp` binary can run in a Zerops service or on your laptop. The user choice is remote setup or local setup. + A Zerops service (`zcp@1`) inside the project runs ZCP. Enable **Include Coding Agent** to add the bundled agent CLI and preconfigure it to use ZCP. Enable Cloud IDE when you want browser VS Code. **Recommended starting point.** [Set up →](/zcp/setup/hosted-workspace) + The `zcp` binary on your laptop, initialized in a project folder with `zcp init`. The agent runs in your local editor or CLI; private service access uses `zcli vpn up`. [Set up →](/zcp/setup/local-agent-bridge) +Either way, the agent gets the same project-scoped ZCP operations against the same project. Remote setup can package ZCP with an agent CLI and browser editor; local setup connects your own agent client to ZCP. +Decision: [Choose remote or local setup](/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 remote setup adds +Remote setup starts with a `zcp@1` Zerops service. Two dashboard options decide what is bundled into that service: +**Include Coding Agent.** Adds the currently supported bundled agent CLI and preconfigures it to use ZCP running inside the `zcp@1` service. Agent authentication is handled separately by that agent's login method. +**Cloud IDE.** Browser-based VS Code (code-server) with 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 → Cloud IDE](/features/local-remote-development#cloud-ide-on-remote-setup). +Without **Include Coding Agent**, the service can still exist, but there is no preconfigured agent waiting inside it. Without **Cloud IDE**, there is no browser editor. With both enabled, you get a one-click agent + IDE setup inside the project. +Both options are configured when you provision the service: [Set up remote ZCP → Path B](/zcp/setup/hosted-workspace#path-b--toggle-the-zcp-service). +## 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. +ZCP is the ergonomic layer on top of Zerops APIs, `zcli`, and SSH for agents. It tightens the loop by packaging project state, guidance, operations, and verification behind one project-scoped interface. +## Operations and permissions +ZCP groups its operations into three categories so an agent client or team policy can gate them: + Inspect state, logs, events, configuration, and verification output. Safe to auto-allow. [Full list →](/zcp/reference/mcp-operations#read-only-operations) + Deploy, change env vars, manage lifecycle, scale, or delete services. Require team policy and confirmation where needed. [Full list →](/zcp/reference/mcp-operations#mutating-operations) + Set up infrastructure, mounts, imports, work sessions, or delivery configuration. Gated by team policy. [Full list →](/zcp/reference/mcp-operations#operational-setup) +That's the surface. The boundary is the project. The token authorizes one project, full stop — cross-project, account-wide, or organization actions stay outside reach. Integration tokens are still bounded by the permissions of the user who created them and by the project access selected for the token; see [Roles & Permissions](/features/rbac). Full blast-radius framing: [Trust model](/zcp/security/trust-model). +Full operation list and permission semantics: [Advanced operations](/zcp/reference/mcp-operations). Token shape and confirmation gates: [Tokens and credentials](/zcp/security/tokens-and-project-access). +## Customize remote setup +Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product - install packages, run additional processes alongside the agent, or ship a hardened version with team-standard tools baked in. Patterns: [Customize remote setup](/zcp/setup/hosted-workspace#customize-remote-setup). +Local setup isn't extensible the same way; the binary on your laptop is just ZCP - editor, shell, and dev server stay your tools' job. +## Source control +When the agent runs in remote setup, 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. In local setup, 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). +## What ZCP is not +ZCP is not a prompt-to-prototype generator, an isolated code sandbox, or an editor-only cloud development environment. It is the control plane that gives a coding agent project-scoped infrastructure operations on Zerops: services, env vars, deploys, logs, verification, recovery, and delivery handoff. +## Where to start + +---------------------------------------- + # Features > Container Vs Vm Ever wondered why container technologies like Docker took over the development world so quickly? Let's break down the real differences between traditional VMs and containers - and why you might want to use one over the other. @@ -6652,27 +6434,16 @@ Think of it like this: At Zerops, we use **containers** as our primary runtime environment - they're fast, efficient, and perfect for most modern development workflows. We've optimized our container infrastructure to handle nearly every type of application you might need to run. However, we also provide **VMs** when you need them, particularly for Docker-based workloads where the additional isolation is essential. Docker containers are a special case - in Zerops, they actually need to run inside VMs for proper security and isolation. While it's technically possible to run Docker in containers using privileged mode, this creates security vulnerabilities. ### When to Use What - Go with Containers when: - Building modern web applications - Working with microservices - Need quick deployment and vertical scaling - Want efficient resource usage - Consider VMs when: - Running legacy applications - Need complete OS isolation - Require specific hardware access - Need to run Docker containers - ### Resource Allocation Both containers and VMs in Zerops can have guaranteed resources: - Specific CPU cores @@ -6706,7 +6477,6 @@ The entire build process, including any time spent in debug mode, has a maximum ::: ## Configuration The debug mode configuration can be found in your service detail under the **Pipelines & CI/CD settings**. - ## Debug Control When execution is paused in debug mode, you have several commands available to control the debugging process. Each command serves a specific purpose and affects the deployment process differently. ### Debug Pause Points @@ -6721,37 +6491,27 @@ To proceed with the normal deployment process, use: ```bash zsc debug continue ``` - Pause Point Behavior - ↪ Before First Command Begins running commands for the current phase until next possible pause point - ✖ On Command Failure Skips the failed command and continues deployment - ✔ After Last Command Moves to the next phase (from build to runtime prepare) or completes deployment - #### Marking Success To force a successful deployment status, use: ```bash zsc debug success ``` - Pause Point Behavior - ↪ Before First Command Ends current phase without running any commands - ✖ On Command Failure Ignores the failure and ends current phase with success - ✔ After Last Command Concludes current phase with a successful status - :::note Requires valid `deployFiles` to work properly (fails otherwise). ::: @@ -6760,41 +6520,30 @@ To terminate the deployment with a failure status, use: ```bash zsc debug fail ``` - Pause Point Behavior - ↪ Before First Command Marks current phase as failed without running commands - ✖ On Command Failure Ends deployment with original error - ✔ After Last Command Overwrites successful execution with failed status and ends deployment - Each phase can be configured independently to pause at any of the points described above, giving you precise control over your debugging workflow. The 60-minute timeout ensures deployments don't remain blocked indefinitely. ## Usage Examples ### Example 1: Debugging Build Failures - Build phase ✖ On Command Failure - Prepare runtime phase ➠ Disable - This configuration allows you to: 1. Inspect the container state after a failure 2. Make necessary adjustments 3. Use `zsc debug continue` to resume or `zsc debug fail` to abort ### Example 2: Validating Runtime Setup - Build phase ➠ Disable - Prepare runtime phase ✔ After Last Command - ## Best Practices #### Targeted Debugging - Enable debug mode only for the specific phase you need to investigate @@ -6856,7 +6605,6 @@ Navigate to service details and find **Environment variables** in the menu. You - Add individual variables using the "Add secret variable" button - Edit individual variables through the menu that appears on hover - Use the bulk editor for managing multiple variables in .env format - ##### Import Configuration Create secret variables for a service with `envSecrets` attribute. See the complete [import.yaml structure](/references/import). ```yaml title="import.yaml" @@ -6907,16 +6655,12 @@ A security feature that controls the **visibility** of environment variables acr By default, Zerops isolates environment variables between services to enhance security and prevent unintended access to sensitive information. This isolation can be configured at both project and service levels. ### Isolation Modes Zerops supports two isolation modes: - Mode Description - service Default mode. Variables are isolated to their respective services. Services can only access their own variables and must explicitly reference variables from other services. - none Legacy mode. All variables from all services are automatically shared and accessible via prefixing. - ### Configuring Isolation #### Project-Level Isolation Zerops automatically creates the `envIsolation` project variable with the default value `service`. You only need to modify this if you want to disable isolation: @@ -7082,38 +6826,27 @@ Enterprise-grade infrastructure with separated core services across multiple con Production applications, high-traffic websites, mission-critical business applications, teams requiring maximum uptime. ::: #### Features Comparison - Lightweight Core Serious Core - Infrastructure Single container (limited redundancy) Multi-container (highly available) - SSL Termination - Automatic Certificate Generation - Proxy / Load Balancer - IPv6 Address - Build Time 15 hours 150 hours - Backup Space 5 GB 25 GB - Egress 100 GB 3 TB - Failover Protection Limited Comprehensive - For detailed pricing information on both core types, visit our [pricing page](/company/pricing#project-core-plans). #### Project Core Upgrade You can upgrade from Lightweight Core to Serious Core for enhanced reliability and increased resources. @@ -7152,6 +6885,103 @@ Containers are the most granular level of the Zerops architecture. Each service ---------------------------------------- +# Features > Local Remote Development + +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. Across local VPN, Cloud IDE, and SSH, you use the same project network, hostnames, service types, and deploy pipeline. +This is also what narrows the gap between development and production. Development and staging can use the same Zerops-managed service types, networking model, and `zerops.yaml` patterns production uses. Production should still live in a separate project with separate credentials, access rules, and resource policies. +:::note +Remote setup is a `zcp@1` service you can add to any project. It can provide a Linux dev container with a curated toolchain, optional Cloud IDE, and optional **Include Coding Agent** wiring. The modes below can use that service, connect directly to runtime services, or stay fully local over VPN; local development with `zcli vpn up` does not 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. Remote setup isn't required. +- **Cloud IDE.** 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 the `zcp` service, or directly to a service container. +Same `db:5432`, same `api:3000`, same project credentials in all three. Switch modes without changing anything about the development 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: add remote setup, 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 service type as stage or production. +This mode is also where coding agents running on your laptop fit. Without ZCP, a local agent client inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. To let the agent operate the project - discover, deploy, verify, read logs, and manage delivery handoff - set up [local ZCP](/zcp/setup/local-agent-bridge). +**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). +::: +## Cloud IDE on remote setup +**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 remote setup (a `zcp@1` service) to a project — see [Set up remote ZCP](/zcp/setup/hosted-workspace) — you get an Ubuntu-based dev container with `zcp`, `zcli`, GitHub CLI, database CLIs, shell utilities, browser automation tools, and SSHFS. Enable **Cloud IDE** for browser-based VS Code; enable **Include Coding Agent** for the bundled agent CLI plus ZCP wiring. +Remote setup can also mount 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 Zerops deploy pipeline. One remote setup service 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.** Remote 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 the `zcp` service's secret environment variables; `git` reads it through a credential helper. +Either pattern keeps your token in the `zcp` service, 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 the `zcp` service +``` +In this mode, remote setup 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. Remote setup becomes a *convenience* layer: a single container that carries the toolchain, mounts multiple dev services over SSHFS, and can include `zcli`, Cloud IDE, and coding-agent wiring for the project. If you'd rather connect your editor directly to your runtime services and skip remote setup 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 the `zcp` service) 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 | Native IDE over SSH | +|---|---|---|---| +| Editor runs | Local | Browser | Local | +| Toolchain | Local | Remote ZCP | Remote ZCP or service container | +| Dev databases | Hostname via VPN | Native, on private network | Native, on private network | +| Remote ZCP 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 | +| 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 remote setup are hitting the same `db:5432`. +For the agent-driven path, the setup decision (remote `zcp@1` service vs. local `zcp` binary) is on its own page: [Choose remote or local setup](/zcp/setup/choose-workspace). +## Same architecture, separate production +The development environments above don't approximate production by inventing a different platform. A ZCP development project can run dev and stage services with the same Zerops-managed service types, private networking, load-balancer model, and `zerops.yaml` patterns that production uses in its separate project. Dev, stage, and production differ in resource allocation, access rules, and credentials, 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 and stage, you've already tested it against the kind of infrastructure it'll meet in production. The remaining differences are scale, credentials, access policy, and data. +This applies whether the developer is human or an agent — see [ZCP for Coding Agents](/features/coding-agents) for the agent case. +## How this differs from cloud IDEs +If you've used GitHub Codespaces or Gitpod, Cloud IDE 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 use the same platform model.** 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 model: managed Postgres, private networking, and `zerops.yaml`. Production still lives in its own project with its own credentials and policies. +Remote ZCP 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 +- 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 → [ZCP for Coding Agents](/features/coding-agents) +- Set up remote setup → [Set up remote ZCP](/zcp/setup/hosted-workspace) +- Pick remote or local ZCP → [Choose remote or local setup](/zcp/setup/choose-workspace) +- Term reference → [ZCP Glossary](/zcp/glossary) + +---------------------------------------- + # Features > Pipeline export const languages = [ @@ -7222,13 +7052,11 @@ The zerops.yaml in your repository tells Zerops how to build and deploy your app 6. Start your application using `npm start` Learn more about `zerops.yaml` parameters for your runtime: ## Trigger the pipeline - ### Continuous deployment Set up automatic builds triggered by Git events. You can establish continuous deployment in two ways: * **New Service:** Create a new runtime service and connect it to your GitHub or GitLab repository during the service creation process. * **Existing Services:** Go to the service detail and choose **Pipelines & CI/CD settings** from the left menu. Click **Connect with a GitHub repository** or **Connect with a GitLab repository** to link your repository. Once connected, Zerops will automatically build and deploy your application with each push to the selected branch or when you create a new tag. - ### On-demand deployment Trigger builds and deployments manually when needed using either the CLI or GUI. #### Using Zerops CLI @@ -7242,7 +7070,6 @@ In **Pipelines & CI/CD settings** section of your service detail: #### Using import YAML Add `buildFromGit: ` to your service configuration for one-time build during import. See [import documentation](/references/import#service-basic-configuration). ## Build phase - Zerops starts a temporary build container and executes these steps: 1. **Install build environment** - Sets up the runtime and tools 2. **Download source code** - From [GitHub ↗](https://www.github.com), [GitLab ↗](https://www.gitlab.com) or via [Zerops CLI](/references/cli) @@ -7253,23 +7080,18 @@ Zerops starts a temporary build container and executes these steps: Zerops automatically deletes the build container after the build finishes or fails. ### Build hardware resources All runtime services use the same hardware resources for build containers: - HW resource Minimum Maximum - CPU cores 1 5 - RAM 8 GB 8 GB - Disk 1 GB 100 GB - Build containers start with minimum resources and scale vertically up to maximum capacity as needed. :::info Build container resources are not charged. Build costs are covered by the standard Zerops [project fee](https://zerops.io/#pricing). @@ -7280,7 +7102,6 @@ The entire build pipeline has a **1 hour** time limit. After 1 hour, Zerops term All runtime services start with a default build environment based on the [build.base](/zerops-yaml/specification#base-) attribute in `zerops.yaml`. Install additional packages or tools by adding [build.prepareCommands](/zerops-yaml/specification#preparecommands-) to your configuration. Learn more about customizing build environments: ## Runtime prepare phase (optional) - When your application requires additional system packages, libraries, or tools in the runtime environment, Zerops allows you to build a custom runtime image. This optional phase occurs after the build phase and before deployment. ### When to use custom runtime images Build custom runtime images when you need: @@ -7343,7 +7164,6 @@ Do not include your application code in the custom runtime image, as your built Shared storage mounts are also not available during the runtime prepare phase. ::: ## Deploy phase - ### Application artifacts After the [build phase](#build-phase) completes, Zerops stores the application artifact in internal storage and deletes the build container. For [manual deployments](#manual-deploy-using-zerops-cli) using Zerops CLI, the application artifact is also uploaded to internal storage. @@ -7385,7 +7205,6 @@ If readiness checks fail for 5 minutes, Zerops marks the container as failed, de - `exec.command` - Succeeds when command returns status code 0 (5-second timeout) Read the [runtime log](/nodejs/how-to/logs#runtime-log) to troubleshoot failed readiness checks. ## Manual deploy using Zerops CLI - Start deploy-only pipelines using the [Zerops CLI](/references/cli). The `zcli service deploy` command uploads and deploys your application in Zerops. Use this when you have your own build process. For building applications in Zerops, use [continuous](#continuous-deployment) or [on-demand](#on-demand-deployment) deployment instead. ```sh Usage: @@ -7413,14 +7232,12 @@ You can modify the deploy pipeline anytime by updating the `zerops.yaml` in your ## Manage builds and deployments ### Cancel running build When you need to cancel an incorrect running build, use the Zerops GUI. Go to the service detail, open the running processes list, and click **Open pipeline detail**. Then click **Cancel build**. - :::caution Build cancellation is only available before the build pipeline finishes. Once the build completes, deployment cannot be cancelled. ::: ### Application versions Zerops keeps the 10 most recent versions of your application in internal storage. Access the application versions list in Zerops GUI by going to service detail and choosing the **Pipelines & CI/CD settings** section from the left menu. The active version is highlighted - click the button below to show all archived versions. - Access pipeline details from the additional menu. Pipeline details contain: - Pipeline configuration (`zerops.yaml`) used for the selected version - Build log (if available) @@ -7443,37 +7260,31 @@ The primary role assigned to a user, determining their **default permissions** a An optional per-project permission that **supersedes the organization role** for that specific project and its resources (services, containers, environment variables, routing, etc.). ## User Roles Zerops supports four user roles, listed from highest to lowest permission level: - Role Organization Access Default Project Access Can Create Projects Can Manage Team - Owner Full (incl. billing) Full ✓ All roles - Admin Full (excl. billing) Full ✓ Developer, Guest - Developer None None (per-project only) ✓ ✗ - Guest None None (per-project only) ✗ ✗ - ### Owner :::note Every organization must have at least one Owner at all times. @@ -7571,48 +7382,37 @@ A Guest user (perhaps an external contractor) can be granted Read only or Full a :::note Owners and Admins cannot be set to "No access" on any project — the minimum override available for these roles is Read only. ::: - Organization Role Without Override With Full Access Override With Read Only Override - Owner Full access Full access Read only - Admin Full access Full access Read only - Developer No access Full access Read only - Guest No access Full access Read only - ## Integration Tokens Integration tokens provide programmatic access to the Zerops API, ideal for CI/CD pipelines, automation scripts, and third-party integrations. ### Token Access Levels - Token Type Description - Full access to all projects Can perform all operations on all current and future projects - Read access to all projects Can view all projects and their resources, but cannot make changes - Custom access per project No default access; each project must be explicitly added with Full or Read only permission - ### Creating Tokens Access token management at **Settings → Access Tokens Management**. 1. Click **Create Token** @@ -7625,22 +7425,16 @@ The token value is displayed only once upon creation. Store it securely — you ::: ### Token Permission Constraints Tokens cannot have higher permissions than the user who creates them: - Your Role Available Token Types - Owner Full access, Read access, Custom per project - Admin Full access, Read access, Custom per project - Developer Custom per project only - Guest Custom per project only - ### Managing Tokens :::note Admins can manage tokens created by Developers and Guests, but cannot manage tokens created by other Admins or Owners. @@ -7657,35 +7451,27 @@ For complete API documentation, see the [Zerops OpenAPI Specification](/referenc ::: ### Backend Role System The API uses a more granular role system than the dashboard interface: - Dashboard Role API Role Code Notes - Owner OWNER Includes canViewFinances, canEditFinances, and canCreateProjects flags (always enabled) - Admin ADMIN Includes canCreateProjects flag (always enabled) - Developer NO_ACCESS With canCreateProjects: true - Guest NO_ACCESS With canCreateProjects: false - — BASIC_USER Available via API only - — READ_ONLY Available via API only - ### Additional Roles (API Only) **BASIC_USER** - Can view all projects (unless overridden to `NO_ACCESS`) @@ -7701,19 +7487,14 @@ The API uses a more granular role system than the dashboard interface: - Sensitive environment variables displayed as `REDACTED` ### Permission Flags The API supports additional permission flags that can be set on any user regardless of role: - Flag Description - canViewFinances Allows viewing billing information, invoices, and payment sources - canEditFinances Allows modifying billing information and processing payments (automatically enables canViewFinances) - canCreateProjects Allows creating new projects (user receives OWNER role on projects they create) - These flags enable scenarios like: - An Admin who needs to view (but not edit) billing information - A Developer who should be able to create projects but also view invoices @@ -7772,31 +7553,26 @@ Docker services do not support automatic vertical scaling. Resource values can b - Managed by Zerops (no application changes needed) ### At-a-Glance Comparison * ✓ = Available *(configurable, defaults vary according to service type)* - Feature Runtime Services & Linux Containers Databases Shared Storage Docker - Automatic Resource Scaling ✓ ✓ ✓ Manual (triggers VM restart) - Automatic Horizontal Scaling ✓ Fixed # of containers Fixed # of containers ✓ - High Availability User-implemented Zerops-managed HA mode Zerops-managed HA mode User-implemented - ## When to Configure Scaling You can configure scaling settings at three different stages: - **During service creation** - Configure initial scaling parameters when creating services in the Zerops GUI. Set resource limits, CPU mode, and container counts from the start. @@ -7877,47 +7653,38 @@ Below are the parameters that control this behavior across all services that sup - **Scale-Down Threshold Percentile:** The usage percentile that triggers resource scaling down - **Minimum Step:** The smallest increment by which resources can increase during scaling - **Maximum Step:** The largest possible increment for resources when scaling rapidly under high load - Parameter CPU RAM Disk - Data Collection Interval 10 seconds 10 seconds 10 seconds - Scale-Up Window Interval 20 seconds 10 seconds 10 seconds - Scale-Down Window Interval 60 seconds 120 seconds 300 seconds - Scale-Up Threshold Percentile 60 50 50 - Scale-Down Threshold Percentile 40 50 50 - Minimum Step 1 (0.1 cores) 0.125 GB 0.5 GB - Maximum Step 40 32 GB 128 GB - ## Part 2: Container Architecture — Service-Specific Approaches Container architecture in Zerops defines how services are distributed across containers. Different service types use fundamentally different approaches: 1. **Horizontal Scaling** (Runtime Services, Linux Containers, and Docker) @@ -8536,25 +8303,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -8831,68 +8592,40 @@ The `project:` section is required. Only one project can be defined. | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Gleam and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Gleam service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -8908,7 +8641,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Gleam service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -9016,7 +8748,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-gleam), a **_recipe_**, containing the most simple Gleam web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Gleam app running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-gleam/blob/main/zerops-project-import.yaml)): ```yaml @@ -9425,19 +9156,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a Go service with hostname = "app" and port = 8080 from another service of the same project, simply use `app:8080`. Read more about [how to access a Go service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the Go runtime environment by installing additional dependencies or tools to the runtime base environment. The base Go environment contains {data.alpine.default}, the selected @@ -9564,25 +9290,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -9604,16 +9324,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Go application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -9656,25 +9372,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -9697,16 +9407,12 @@ Read more about how the [readiness check works](/go/how-to/deploy-process#readin #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Go application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -9852,70 +9558,39 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description - hostname - The unique service identifier. - The hostname of the new database will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Go service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -9931,7 +9606,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Go service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -10039,7 +9713,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-go-hello-world), a **_recipe_**, containing the most simple Go web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Go running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-go-hello-world/blob/main/import-project/description.yaml)): ```yaml @@ -10297,22 +9970,27 @@ zsc cdn purge /style.css$ # Purge exact file # Guides > Choose Queue -**Use NATS** for most cases (simple, fast, JetStream persistence). Use **Kafka** only for enterprise event streaming with guaranteed ordering and unlimited retention. +**Use NATS** for most cases (simple, fast, optional JetStream persistence layer when durability is needed). Use **Kafka** only for enterprise event streaming with guaranteed ordering and unlimited retention. ## Decision Matrix | Need | Choice | Why | |------|--------|-----| -| **General messaging** | **NATS** (default) | Simple auth, JetStream built-in, fast | +| **General messaging** | **NATS** (default) | Simple auth, fast, JetStream available when needed | | Enterprise event streaming | Kafka | SASL auth, 3-broker HA, unlimited retention | -| Lightweight pub/sub | NATS | Low overhead, 8MB default messages | +| Lightweight pub/sub | NATS — core | Low overhead, 8MB default messages, fire-and-forget | +| Durable queues, replay, at-least-once | NATS — JetStream | Persistent streams, durable consumers, ack/redeliver | | Event sourcing / audit logs | Kafka | Indefinite topic retention, strong ordering | | Event sourcing / audit logs | Kafka | Indefinite topic retention, strong ordering | ## NATS (Default Choice) +NATS exposes **two distinct messaging shapes**. Pick ONE per recipe and write yaml comments / KB content describing only that shape — mixing them confuses porters about what the recipe actually does. +- **Core pub/sub + queue groups**: `nc.subscribe('subject', { queue: 'workers' })`. No persistence; queue groups load-balance delivery across replicas; lost messages stay lost. HA story: surviving cluster nodes keep delivering, no consumer position to restore. Use when fan-out + load balance + at-most-once is enough. +- **JetStream streams + durable consumers**: opens an explicit stream via `JetStreamManager`, subscribes durably via `js.subscribe(...)`. Persistent message store; replay on reconnect; ack/redeliver. HA story: cluster replicates stream state, acked-but-unprocessed messages survive node loss. Use when at-least-once + replay + persistence are required. +**Authoring rule**: a recipe's yaml comments and KB bullets should reflect the shape the code actually uses. If the worker only calls `nc.subscribe()` with a queue group and never opens a stream, do not invoke JetStream language at HA tiers — the recipe has no stream to replicate. If the worker opens a JetStream stream, the JetStream HA story is the relevant one. - Ports: 4222 (client), 8222 (HTTP monitoring) - Auth: user `zerops` + auto-generated password - **Connection** — two supported patterns, pick ONE: - **Separate env vars** (recommended, works with every NATS client library): pass `servers: ${hostname}:${port}` plus `user: ${user}, pass: ${password}` as client-side connect options. The servers list stays credential-free. - **Opaque connection string**: pass `${connectionString}` directly as the servers option — the platform builds a correctly-formatted URL with embedded auth that the NATS server expects. -- JetStream: Enabled by default (`JET_STREAM_ENABLED=1`) +- JetStream capability: enabled by default (`JET_STREAM_ENABLED=1`); recipes opt in by writing JetStream client code. Setting `JET_STREAM_ENABLED=0` hard-disables the capability across the project. - Storage: Up to 40GB memory + 250GB file store - Max message: 8MB default, 64MB max (`MAX_PAYLOAD`) - Health check: `GET /healthz` on port 8222 @@ -10328,7 +10006,7 @@ zsc cdn purge /style.css$ # Purge exact file ## Gotchas 1. **NATS config changes need restart**: No hot-reload — changing env vars requires service restart 2. **Kafka single-node has no replication**: 1 broker = 3 partitions but zero redundancy -3. **NATS JetStream HA sync interval**: 1-minute sync across nodes — brief data lag possible +3. **NATS JetStream HA sync interval**: 1-minute sync across nodes — brief data lag possible. Applies only to recipes that actually open JetStream streams; core pub/sub recipes are unaffected. 4. **Kafka SASL only**: No anonymous connections — always use the generated credentials 5. **NATS authorization violation from a hand-composed URL**: do not build a `nats://user:pass@host:4222` URL from the separate env vars. Most NATS client libraries will parse the embedded credentials AND separately attempt SASL with the same values, producing a double-auth that the server rejects with `Authorization Violation` on the first CONNECT frame (symptom: startup crash, no successful subscription). Use either the separate env vars passed as connect options (credential-free servers list) or the opaque `${connectionString}` the platform builds for you — both patterns in the Connection section above avoid the double-auth path. @@ -10434,7 +10112,7 @@ jobs: - uses: zeropsio/actions@main with: access-token: ${{ secrets.ZEROPS_TOKEN }} - service-id: + service-id: ``` - `access-token`: From Settings → Access Token Management - `service-id`: From service URL or three-dot menu → Copy Service ID @@ -10490,7 +10168,7 @@ Always use **Full (strict)** SSL mode in Cloudflare — "Flexible" causes redire ## DNS Configuration ### CNAME (non-apex or with CNAME flattening) ``` -CNAME +CNAME ``` ### With Cloudflare Proxy (orange cloud) | IP Type | Record | Proxy | @@ -10509,7 +10187,7 @@ CNAME ## Wildcard Domains ``` Method A: A *. + AAAA *. -Method B: CNAME *. +Method B: CNAME *. ACME: CNAME _acme-challenge. .zerops.zone ``` ## SSL/TLS Settings (Cloudflare Dashboard) @@ -10732,7 +10410,7 @@ run: db_hostname: ${db_hostname} # SELF-SHADOW — see next section db_password: ${db_password} # SELF-SHADOW queue_hostname: ${queue_hostname} # SELF-SHADOW - STAGE_API_URL: ${STAGE_API_URL} # SELF-SHADOW (project-level variant) + API_URL: ${API_URL} # SELF-SHADOW (project-level variant) ``` The referenced variable does **not** need to exist at definition time — Zerops resolves at container start. ### Self-Shadow Trap @@ -10744,7 +10422,7 @@ run: db_password: ${db_password} # OS env: db_password='${db_password}' (literal) ``` At runtime, the worker tries to connect to `"${db_hostname}:5432"` and crashes. The fix is to **delete the entire block** — those vars are already in the container's env without any declaration. -This applies identically to project-level vars (`${STAGE_API_URL}`, `${APP_SECRET}`) and cross-service vars (`${db_hostname}`, `${queue_user}`) — both auto-propagate, both self-shadow under the same rule. +This applies identically to project-level vars (`${API_URL}`, `${APP_SECRET}`) and cross-service vars (`${db_hostname}`, `${queue_user}`) — both auto-propagate, both self-shadow under the same rule. **Hostname transformation**: dashes become underscores. Service `my-db` variable `port` is `${my_db_port}`. ### Cross-Service References in API vs Runtime Cross-service references (`${hostname_varname}`) are **resolved at container start time**, not at definition time. This means: @@ -10773,26 +10451,26 @@ Project variables are **automatically available in every service, in both runtim ```yaml build: buildCommands: - - echo "building for $STAGE_API_URL" # shell reads the OS env var - - VITE_API_URL=$STAGE_API_URL npm run build # or pass it forward by shell prefix + - echo "building for $API_URL" # shell reads the OS env var + - VITE_API_URL=$API_URL npm run build # or pass it forward by shell prefix ``` **In `build.envVariables` YAML** (to compose a derived var that the bundler consumes) reference the project var directly without prefix: ```yaml build: envVariables: - VITE_API_URL: ${STAGE_API_URL} # project var STAGE_API_URL read as-is, NO RUNTIME_ prefix + VITE_API_URL: ${API_URL} # project var API_URL read as-is, NO RUNTIME_ prefix ``` **In `run.envVariables` YAML** (to forward a project var under a framework-conventional name without creating a shadow), reference directly without prefix: ```yaml run: envVariables: - FRONTEND_URL: ${STAGE_FRONTEND_URL} # project var STAGE_FRONTEND_URL forwarded as FRONTEND_URL + CORS_ALLOWED_ORIGIN: ${FRONTEND_URL} # project var FRONTEND_URL forwarded under a different name ``` **DO NOT** re-reference an auto-injected variable under its SAME name — that's a self-shadow loop. Applies to BOTH project-level vars AND cross-service vars: ```yaml envVariables: PROJECT_NAME: ${PROJECT_NAME} # project-level self-shadow - STAGE_API_URL: ${STAGE_API_URL} # project-level self-shadow + API_URL: ${API_URL} # project-level self-shadow db_hostname: ${db_hostname} # cross-service self-shadow queue_user: ${queue_user} # cross-service self-shadow ``` @@ -10808,10 +10486,10 @@ Dual-runtime recipes (frontend SPA + backend API on the same platform) use proje ```yaml project: envVariables: - STAGE_API_URL: https://apistage-${zeropsSubdomainHost}-3000.prg1.zerops.app - STAGE_FRONTEND_URL: https://appstage-${zeropsSubdomainHost}.prg1.zerops.app + API_URL: https://apistage-${zeropsSubdomainHost}-3000.prg1.zerops.app + FRONTEND_URL: https://appstage-${zeropsSubdomainHost}.prg1.zerops.app ``` -The platform resolves `${zeropsSubdomainHost}` when injecting the value into services at container start. The frontend consumes `STAGE_API_URL` via plain `${STAGE_API_URL}` in `build.envVariables` (baking it into the bundle at compile time) — **no `RUNTIME_` prefix**. The API consumes `STAGE_FRONTEND_URL` via plain `${STAGE_FRONTEND_URL}` in `run.envVariables` (for CORS allow-list). The same names must be set on the workspace project via `zerops_env project=true action=set` after provision, so workspace verification doesn't see literal `${STAGE_FRONTEND_URL}` strings. +The platform resolves `${zeropsSubdomainHost}` when injecting the value into services at container start. The frontend consumes `API_URL` via plain `${API_URL}` in `build.envVariables` (baking it into the bundle at compile time) — **no `RUNTIME_` prefix**. The API consumes `FRONTEND_URL` via plain `${FRONTEND_URL}` in `run.envVariables` (for CORS allow-list). The same names must be set on the workspace project via `zerops_env project=true action=set` after provision, so workspace verification doesn't see literal `${FRONTEND_URL}` strings. ## Secret Variables - Defined via GUI, import.yml `envSecrets`, or `dotEnvSecrets` - **Write-only after creation** -- values masked in GUI, cannot be read back via API @@ -10852,7 +10530,7 @@ Env var changes (secret or project) take effect only on container start. The run ## System-Generated Variables Zerops auto-generates variables per service (e.g., `hostname`, `PATH`, DB connection strings). Cannot be deleted. Some read-only (`hostname`), others editable (`PATH`). Can be referenced by other services using `${hostname_varname}`. ## Common Mistakes -- **DO NOT** re-reference auto-injected vars under their own name — self-shadow loop. Applies to BOTH project-level (`STAGE_API_URL: ${STAGE_API_URL}`) AND cross-service (`db_hostname: ${db_hostname}`, `queue_user: ${queue_user}`). +- **DO NOT** re-reference auto-injected vars under their own name — self-shadow loop. Applies to BOTH project-level (`API_URL: ${API_URL}`) AND cross-service (`db_hostname: ${db_hostname}`, `queue_user: ${queue_user}`). - **DO NOT** declare cross-service vars you only want to READ — they are already in the container's OS env. Read via `process.env.db_hostname` / `getenv('db_hostname')` directly. Declare in `run.envVariables` only to RENAME (e.g. `DB_HOST: ${db_hostname}`) or to set mode flags. - **DO NOT** forget restart after GUI/API env changes — process won't see new values - **DO NOT** expect `envReplace` to recurse subdirectories — it does not @@ -10895,113 +10573,6 @@ Contact `support@zerops.io` with Project ID + Organization ID to request changes ---------------------------------------- -# Guides > Local Development - -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` | -| 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"` | -| 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 - ----------------------------------------- - # Guides > Logging Zerops captures stdout/stderr as logs; use syslog output format for severity filtering. Supports forwarding to Better Stack, Papertrail, or self-hosted ELK via syslog. @@ -11071,7 +10642,7 @@ envVariables: ELASTIC_APM_ACTIVE: "true" ELASTIC_APM_SERVICE_NAME: my-app ELASTIC_APM_SERVER_URL: https://apmserver.zerops.app - ELASTIC_APM_SECRET_TOKEN: + ELASTIC_APM_SECRET_TOKEN: ``` ## Prometheus + Grafana Stack Services | Service | Purpose | @@ -11795,7 +11366,7 @@ envVariables: SMTP_PORT: "587" SMTP_USER: apikey envSecrets: - SMTP_PASSWORD: + SMTP_PASSWORD: ``` ## Gotchas 1. **Port 25 is permanently blocked**: Cannot be unblocked — use 587 with STARTTLS @@ -11804,6 +11375,55 @@ envSecrets: ---------------------------------------- +# Guides > Verify Web Agent Protocol + +Sub-agent dispatch protocol for end-to-end verification of a Zerops web +service. The main agent reads `develop-verify-matrix` (atom) for which +services need this protocol; the protocol body itself lives here so it +ships only when fetched, not on every per-turn payload. +Spawn one sub-agent per web-facing target. Substitute `{targetHostname}` +and `{runtime}` with that service's values when constructing the prompt. +--- +## Sub-agent dispatch prompt +``` +Agent(model="sonnet", prompt=""" +Verify Zerops service "{targetHostname}" ({runtime}) works for end users. +## Protocol +1. `zerops_verify serviceHostname="{targetHostname}"` — infrastructure baseline +2. If NOT healthy → VERDICT: FAIL (cite failed checks from zerops_verify response) +3. `zerops_discover service="{targetHostname}"` — get subdomainUrl or connection info +4. Determine reachable URL: + - subdomainUrl available → use it (public HTTPS) + - no subdomain, no custom domain → VERDICT: UNCERTAIN (cannot reach from outside) + - unreachable after timeout → VERDICT: UNCERTAIN +5. `agent-browser open {url}` +6. `agent-browser snapshot` — accessibility tree for AI analysis +7. Evaluate: does the page render meaningful content? + - Interactive elements (buttons, links, forms)? + - Text content (headings, paragraphs)? + - Or empty/broken (empty root div, error page, blank screen)? +8. If concerns: `agent-browser eval "JSON.stringify(Array.from(document.querySelectorAll('script[src]')).map(s=>s.src))"` for loaded scripts +9. For SPAs: `agent-browser eval "window.__errors || []"` AND check if console has errors +## Rules +- zerops_verify unhealthy/degraded → always VERDICT: FAIL (never override infra checks) +- HTTP 401/403 with rendered content (login page, auth challenge) → VERDICT: PASS (auth is working correctly) +- HTTP 401/403 with empty body → VERDICT: UNCERTAIN (cannot determine if intentional) +- zerops_verify healthy + page empty/broken → VERDICT: FAIL (cite what you see) +- zerops_verify healthy + page renders real content → VERDICT: PASS +- agent-browser unavailable or URL unreachable → VERDICT: UNCERTAIN +## Output (mandatory format) +### Infrastructure +zerops_verify status and check summary +### Application +what you observed — DOM content, JS errors, visual state +### Evidence +accessibility tree excerpt or error details +### VERDICT: PASS or FAIL or UNCERTAIN — one-line justification +""") +``` + +---------------------------------------- + # Guides > Vpn Zerops VPN uses WireGuard via `zcli vpn up ` — connects to one project at a time, services accessible by hostname, but env vars are NOT available through VPN. @@ -11988,37 +11608,31 @@ In the meantime, if you have any troubles setting up your application, or if you Get quick answers to your related questions about Zerops from frequently asked questions we get asked. ### General Question: Where can I find the Zerops Dashboard GUI? -Answer: +Answer: You can access the Zerops Dashboard GUI directly at app.zerops.io. - Question: How much does it cost to get started? -Answer: +Answer: It's free to get started, and no credit card is required! However, we recommend visiting our pricing page to explore the options that best suit your needs. We also have a calculator on our pricing page that can help you estimate the cost of your project. - Question: Where are your servers located? -Answer: +Answer: Our infrastructure spans multiple regions. In Europe, we operate our own high-tier - data center in Prague, Czech Republic, running on bare metal servers managed by + data center in **Prague**, Czech Republic, running on bare metal servers managed by vshosting's senior admin team — vshosting.eu, one of the largest providers of managed hosting in Europe. In the US, we have servers - in New York, with additional US East Coast capacity on its way through a trusted + in **New York**, with additional US East Coast capacity on its way through a trusted infrastructure partner. Stay tuned for updates on our Discord server and checkout our roadmap! - Question: Why should I use Zerops over Self-Hosted PaaS? -Answer: +Answer: We have a detailed article discussing whether you should go for a Self-hosted PaaS → [The rise of self-hosted PaaS — is $5 VPS all you need?](https://zerops.io/article/the-rise-of-self-hosted-paa-s-is-5-vps-all-you-need). - Question: How do I change my email? -Answer: +Answer: Navigate to the main menu in the Zerop GUI (with your icon) and add a new user with the selected email to your team. - Question: I have more questions. Where can I reach out to get help? -Answer: +Answer: You can reach us on our Discord server for support. For additional contact options, please visit our contacts page. - ---------------------------------------- @@ -12473,19 +12087,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a Java service with hostname = "app" and port = 8080 from another service of the same project, simply use `app:8080`. Read more about [how to access a Java service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customizes the Java runtime environment by installing additional dependencies or tools to the runtime base environment. The base Java environment contains {data.alpine.default}, the selected major @@ -12613,25 +12222,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -12653,16 +12256,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Java application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -12705,25 +12304,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -12746,16 +12339,12 @@ Read more about how the [readiness check works](/java/how-to/deploy-process#read #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Java application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -12901,70 +12490,39 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description - hostname - The unique service identifier. - The hostname of the new database will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Java service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -12980,7 +12538,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Java service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -13089,7 +12646,6 @@ Build tools: When you need Maven, Gradle, or other build tools not included by d As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-java-hello-world), a **_recipe_**, containing the most simple Java web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Java running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-java-hello-world/blob/main/import-project/description.yaml)): ```yaml @@ -13372,76 +12928,48 @@ The hostname of the first service will be set to `keydb1`. The highly available The hostname of the second service will be set to `keydb2`. The single container mode will be chosen and the default [auto scaling configuration](/keydb/how-to/scale) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains only KeyDB services but you can create a `description.yaml` with [different types] of services. - Parameter Description - hostname - The unique service identifier. The hostname of the new database will be set to the `hostname` value. Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [KeyDB service types](/references/import-yaml/type-list#database-services) are currently supported. - mode - Defines the operation mode of KeyDB service. HA - Creates a KeyDB cluster with 2 database containers. This mode is suited for production. Zerops always keeps the 2 database containers on different physical machines. All your data is stored redundantly in 2 identical copies. In case of a failure of a container or the underlying physical machine, Zerops automatically disconnects the failed container from the cluster, creates a new container and syncs all data from the remaining copy. Finally the broken container is automatically deleted. NON_HA - Zerops will create a KeyDB database installed in a single container. Useful for non-essential data or dev environments. Your data is stored only in a single container. If the container or the underlying physical machine fails, your data since the last backup are lost. Zerops doesn't provide any automatic repairs of single node KeyDB services. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - :::caution The KeyDB service **hostname** and **mode** are fixed after the service is created. They can't be changed later. ::: @@ -13460,7 +12988,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add KeyDB service to an existing project #### Example Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -13527,7 +13054,6 @@ You can configure scaling settings: - **During import** - Define scaling configuration in your YAML import file using `verticalAutoscaling` parameters - **After service creation** - Navigate to your KeyDB service and select **Automatic scaling configuration** to modify settings ### Basic settings - **CPU Mode**: Choose between shared (cost-effective, variable performance) or dedicated (consistent performance, higher cost). You can change CPU mode once per hour. See [pricing](https://zerops.io/#pricing) for costs. **Resource limits**: Configure minimum and maximum resources for your KeyDB service: - **Lower the maximum** to control costs and prevent over-scaling @@ -13546,7 +13072,6 @@ When a container fails in HA mode, Zerops automatically replaces it with a new c - **Absolute (GB)**: Maintains this amount of free RAM at all times - **Percentage**: Keeps this percentage of total RAM free Consider increasing these values if your database experiences memory-related issues. - :::info Read More For detailed technical parameters and scaling behavior, see [Automatic Scaling and High Availability](/features/scaling#resource-scaling-behavior). ::: @@ -13866,85 +13391,60 @@ The hostname of the first service will be set to `mariadb1`. The highly availabl The hostname of the second service will be set to `mariadb2`. The single container mode will be chosen and the default [auto scaling configuration](/mariadb/how-to/scale) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains only MariaDB services but you can create a `description.yaml` with [different types] of services. - Parameter Description Limitations - hostname A unique service identifier like `mariadb`,`sql`, `db` etc. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type Specifies the service type and version. - See what [MariaDB service types](/references/import-yaml/type-list#database-services) are currently supported. - mode Defines the operation mode of MariaDB service. - HA - Zerops will create a MariaDB cluster with 3 database containers and 2 free database proxies. This mode is suited for production. - Zerops always keep the 3 database containers on different physical machines. All your data is stored redundantly in 3 copies. In case of a failure of a container or the underlying physical machine, Zerops automatically disconnects the failed container from the cluster, creates a new container and syncs all data from the remaining 2 copies. Finally the broken container is automatically deleted. - NON_HA - Zerops will create a MariaDB database installed in a single container. Useful for non-essential data or dev environments. - Your data is stored only in a single container. If the container or the underlying physical machine fails, your data since the last backup are lost. Zerops doesn't provide any automatic repairs of single node MariaDB services. - verticalAutoscaling Defines custom vertical auto scaling parameters - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam Set the minRam or maxRam in GB (float). - - minDisk/maxDisk Set the minDisk or maxDisk in GB (float). - envSecrets Defines [secret environment variables](/features/env-variables#2-secret-variables) for the service - :::caution The MariaDB service **hostname** and **mode** are fixed after the service is created. They can't be changed later. ::: @@ -13963,7 +13463,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add MariaDB service to an existing project #### Example Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -13999,23 +13498,18 @@ Maximum size of the `import.yaml` file is 100 kB. When migrating to MariaDB in Zerops from other database systems, you may need to configure specific system variables to maintain compatibility with your existing applications. #### Setting `lower_case_table_names` for MySQL compatibility The `lower_case_table_names` system variable determines how MariaDB handles table name case sensitivity: - Value Table Name Storage Comparison Behavior - 0 Stored as specified (preserves case) Case-sensitive - 1 Stored in lowercase Case-insensitive - 2 Stored as specified (preserves case) Case-insensitive - :::caution This variable must be configured when the MariaDB instance is first created and cannot be changed afterward. This is a limitation of MariaDB itself, not specific to Zerops. ::: @@ -14189,7 +13683,6 @@ You can configure scaling settings: - **During import** - Define scaling configuration in your YAML import file using `verticalAutoscaling` parameters - **After service creation** - Navigate to your MariaDB service and select **Automatic scaling configuration** to modify settings ### Basic settings - **CPU Mode**: Choose between shared (cost-effective, variable performance) or dedicated (consistent performance, higher cost). You can change CPU mode once per hour. See [pricing](https://zerops.io/#pricing) for costs. **Resource limits**: Configure minimum and maximum resources for your MariaDB service: - **Lower the maximum** to control costs and prevent over-scaling @@ -14208,7 +13701,6 @@ When a container fails in HA mode, Zerops automatically replaces it with a new c - **Absolute (GB)**: Maintains this amount of free RAM at all times - **Percentage**: Keeps this percentage of total RAM free Consider increasing these values if your database experiences memory-related issues. - :::info Read More For detailed technical parameters and scaling behavior, see [Automatic Scaling and High Availability](/features/scaling#resource-scaling-behavior). ::: @@ -14391,15 +13883,12 @@ function SearchComponent() { setResults(data.hits); }; return ( - handleSearch(e.target.value)} placeholder="Search products..." /> - {results.map(hit => ( {hit.name} ))} - ); } ``` @@ -14460,16 +13949,12 @@ You can fine-tune your NATS service by adjusting **environment variables**: :::note If certain variables are not visible in your configuration, they may have been introduced after your service was created. Simply add them as [secret variables](/features/env-variables#2-secret-variables) to access the functionality. ::: - Variable Description - MAX_PAYLOAD Defines the maximum allowed message size for all NATS traffic. Default: 8MB, Maximum: 64MB. See NATS limits documentation for details. - JET_STREAM_ENABLED Controls whether JetStream functionality is enabled. Default: 1 (enabled), Set to 0 to disable. See JetStream Configuration section below for more details. - :::important Configuration changes require a service **restart** to take effect. While NATS itself supports configuration hot-reload, this feature will be implemented in a future Zerops update. ::: @@ -14537,9 +14022,8 @@ For advanced configurations or custom requirements: # Nginx > Faq Question: How do I enable SEO optimization with prerender.io? -Answer: +Answer: Zerops provides built-in prerender.io support. Simply set the `PRERENDER_TOKEN` environment variable with your prerender.io service token. See our [prerender.io documentation](/nginx/how-to/env-variables#prerenderio-support) for details. - ---------------------------------------- @@ -14703,19 +14187,14 @@ For example, to connect to a Nginx static service with hostname = "app" and port Do not use the port **:443**. All the incoming traffic is terminated on the Zerops internal balancer where the SSL certificate is installed and the request is forwarded to your Nginx static service as a **http://** on the port **:80**. ::: Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customizes the Nginx runtime environment by installing additional dependencies or tools to the runtime base environment. The base Nginx environment contains {data.alpine.default}, the selected @@ -14751,7 +14230,6 @@ Some packages or tools can take a long time to install. Therefore, Zerops caches 1. Content of the [build.addToRunPrepare](#copy-folders-or-files-from-your-build-container) and `run.prepareCommands` attributes didn't change from the previous deploy 2. The custom runtime cache wasn't invalidated in the Zerops GUI. To invalidate the Zerops runtime cache go to your service detail in Zerops GUI, choose **Service dashboard & runtime containers** from the left menu and click on the **Open pipeline detail** button. Then click on the **Clear runtime prepare cache** button. - When the prepare cache is used, Zerops doesn't create a prepare runtime container and executes the deployment of your application directly. #### Single or separated shell instances You can configure your prepare commands to be run in a single shell instance or multiple shell instances. @@ -14855,25 +14333,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -14896,16 +14368,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Nginx application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -14933,25 +14401,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -14974,16 +14436,12 @@ Read more about how the [readiness check works](/nginx/how-to/deploy-process#rea #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Nginx application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -15115,84 +14573,51 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" ``` #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains an Nginx static service but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [nginx service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -15208,7 +14633,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Nginx static service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -15270,7 +14694,6 @@ The default Nginx Static runtime environment contains: If your Nginx Static service needs additional tools beyond the default environment (SSL management, monitoring, security tools, etc.), you'll need to build a custom runtime image. Here are Nginx Static-specific examples of configuring custom runtime images in your `zerops.yml`: ### Basic Nginx Static Setup - For complete configuration details, see the [runtime prepare phase configuration guide](/features/pipeline#configuration). ## Process and Caching ### How Runtime Prepare Works @@ -15367,22 +14790,18 @@ The Nginx configuration will automatically handle the rest—no additional confi ## Prerender.io Support Zerops provides built-in prerender.io support for SEO optimization. Configure it using these environment variables: - Variable Required Description Default - PRERENDER_TOKEN Yes Your prerender.io service token - - PRERENDER_HOST No Prerender service host service.prerender.io - :::tip Set `PRERENDER_TOKEN` as a secret environment variable in Zerops GUI for security. ::: @@ -15450,13 +14869,12 @@ Have you build something that others might find useful? Don't hesitate to share # Nodejs > Faq Question: Why is my Node.js build timing out or hanging on interactive prompts? -Answer: +Answer: If your build process seems to be hanging or timing out, check your logs for interactive prompts that are waiting for input, such as: ``` ? The modules directory at "/build/source/node_modules" will be removed and reinstalled from scratch. Proceed? (Y/n) ‣ true ``` Set the environment variable `CI: true` to resolve the problem. This allows the installation to proceed automatically without requiring manual confirmation. - ---------------------------------------- @@ -15837,19 +15255,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a Node.js service with hostname = "app" and port = 3000 from another service of the same project, simply use `app:3000`. Read more about [how to access a Node.js service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the Node.js runtime environment by installing additional dependencies or tools to the runtime base environment. The base Node.js environment contains {data.alpine.current} the selected @@ -15977,25 +15390,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -16017,16 +15424,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Node.js application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -16069,25 +15472,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -16110,16 +15507,12 @@ Read more about how the [readiness check works](/nodejs/how-to/deploy-process#re #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Node.js application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -16265,86 +15658,50 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Node.js and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - The hostname of the new database will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Node.js service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -16360,7 +15717,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Node.js service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -16468,7 +15824,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-nodejs), a **_recipe_**, containing the most simple Node.js web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Node.js app running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-nodejs/blob/main/zerops-project-import.yaml)): ```yaml @@ -16525,24 +15880,17 @@ The bucket quota size was defined when the Object storage service was created. Y ## Copy access details from Zerops GUI You will find the Object storage access details under the **Access details** button in the project dashboard page. The same information is available in the service detail page under the **Access & bucket details** menu. - ### Object storage access parameters - Parameter Description - API URL URL of the Object storage service - Bucket Name Bucket is created with a name based on the selected service name and a random prefix. The name of the bucket is fixed and cannot be changed later. - Access Key ID The S3 Access Key ID - Secret Key The S3 Secret Key - ## Use Object storage environment variables Zerops creates default environment variables for each Object storage service to help you with connection from any runtime services in the same project. To avoid the need to copy Object storage access parameters manually, use environment variables in your runtime service. ### Prefix the environment variable key @@ -16551,36 +15899,25 @@ All services of the same project can reference environment variables from other To access the `bucketName` env variable of the `upload` service, use `upload_bucketName` as the env variable key. To access the `secretAccessKey` env variable of the `storage` service, use `storage_secretAccessKey` as the env variable key. ### Object storage environment variables List of service environment variables is available in Zerops GUI. Go to an Object storage service detail and choose **Environment variables** in the left menu. - Zerops creates following environment variables when the Object storage service is created: - Variable Description - apiUrl URL of the Object storage service - accessKeyId The S3 Access Key ID - secretAccessKey The S3 Secret Key - bucketName Bucket is created with a name based on the selected service name and a random prefix. The name of the bucket is fixed and cannot be changed later. - quotaGBytes The bucket quota in GB. - projectId ID of the project. Generated by Zerops. - serviceId ID of the Object storage service. Generated by Zerops. - hostname The name of the Object storage service. - ---------------------------------------- @@ -16612,41 +15949,28 @@ Each Object storage service can only contain one bucket. If your application nee Bucket will be created with a name based on the given service name and a random prefix. The name of the bucket cannot be changed later. #### Access policy Select one of the basic policy templates: - Template Description - Public read - Allows anyone: - to read the bucket's location `(s3:GetBucketLocation)` to list all bucket's objects `(s3:ListBucket)` to get any object of the bucket `(s3:GetObject)` - Public objects read - Allows anyone: - to read the bucket's location `(s3:GetBucketLocation)` to get any object of the bucket `(s3:GetObject)` - Public read write - Allows anyone: - to read the bucket's location `(s3:GetBucketLocation)` to list all bucket's objects `(s3:ListBucket)` to get any object of the bucket `(s3:GetObject)` to create a new object in the bucket `(s3:PutObject,s3:ListMultipartUploadParts, s3:AbortMultipartUpload, s3:ListBucketMultipartUploads)` to delete any object of the bucket `(s3:DeleteObject)` - Public write Allows anyone to create objects in the bucket `(PutObject action)` - Private Denies the access to unauthenticated users. - Or you can set your own access policy in the [IAM Policy JSON format](https://min.io/docs/minio/linux/administration/identity-access-management/policy-based-access-control.html#policy-document-structure). #### Example: ```yaml @@ -16675,7 +15999,6 @@ The `{{ .BucketName }}` variable will be replaced by the bucket name. The bucket's policy can be changed later in Zerops GUI. #### Quota Set the bucket quota size in GB. The quota must be set manually. It can be changed later in Zerops GUI. Zerops doesn't support Object storage autoscaling. You can set the bucket quota from 1 to 100 GB. - ## Create Object storage using zCLI zCLI is the Zerops command-line tool. To create a new Object storage service via the command-line, follow these steps: 1. [Install & setup zCLI](/references/cli) @@ -16712,76 +16035,47 @@ services: The yaml file describes your future project infrastructure. The project will contain one Object storage service named `upload`. The bucket quota will be set to 73 GB and the bucket access policy will be set to `public-write`. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. Maximum 255 characters - description Optional. Description of the new project. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains only Object storage service but you can create a description.yaml with different types of services. - Parameter Description - hostname - The unique service identifier. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the Object storage type objectstorage and version. - Set type: `objectstorage` - Limitations: - Currently `objectstorage` or `object-storage` is available. - objectStorageSize - The size of the bucket quota in GB. - Limitations: - Set a whole number between 1 and 100. - objectStoragePolicy - Optional. Either `objectStoragePolicy` or `objectStorageRawPolicy` is required. - Set one of allowed values: - `public-read` `public-objects-read` `public-read-write` `public-write` `private` - Read more about the basic policy templates. - objectStorageRawPolicy - Optional. Either `objectStoragePolicy` or `objectStorageRawPolicy` is required. - Set your own access policy in the IAM Policy JSON format. - The `{{ .BucketName }}` variable will be replaced by the bucket name. - Zerops creates one bucket automatically for each new Object storage service. :::note Each Object storage service can only contain one bucket. If your application needs multiple buckets, add more Object storage services. @@ -16802,7 +16096,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Object service to an existing project Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: ```yaml @@ -16905,7 +16198,6 @@ Zerops creates one bucket automatically for each new Object storage service. Each Object storage service can only contain one bucket. If your application needs multiple buckets, add more Object storage services. ::: To change the bucket size or the access policy, go to the **Access & bucket details** in the Object service detail in Zerops GUI, scroll down and click on the **Configure bucket quota size and access policy** button. - ### Quota Set the bucket quota size in GB. The quota must be set manually. It can be changed later in Zerops GUI. Zerops doesn't support Object storage autoscaling. You can set the bucket quota from 1 to 100 GB. ### Access policy @@ -17346,19 +16638,14 @@ For example, to connect to a PHP service with hostname = "app" and port = 80 fro Do not use the port **:443**. All the incoming traffic is terminated on the Zerops internal balancer where the SSL certificate is installed and the request is forwarded to your PHP+Nginx / PHP+Apache service as a **http://** on the port **:80**. ::: Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the PHP runtime environment by installing additional dependencies or tools to the runtime base environment. The base PHP environment contains {data.alpine.default}, the selected @@ -17394,7 +16681,6 @@ Some packages or tools can take a long time to install. Therefore, Zerops caches 1. Content of the [build.addToRunPrepare](#copy-folders-or-files-from-your-build-container) and `run.prepareCommands` attributes didn't change from the previous deploy 2. The custom runtime cache wasn't invalidated in the Zerops GUI. To invalidate the Zerops runtime cache go to your service detail in Zerops GUI, choose **Service dashboard & runtime containers** from the left menu and click on the **Open pipeline detail** button. Then click on the **Clear runtime prepare cache** button. - When the prepare cache is used, Zerops doesn't create a prepare runtime container and executes the deployment of your application directly. #### Single or separated shell instances You can configure your prepare commands to be run in a single shell instance or multiple shell instances. The format is identical to [build commands](#buildcommands). @@ -17499,25 +16785,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -17540,16 +16820,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your PHP application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -17593,25 +16869,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -17634,16 +16904,12 @@ Read more about how the [readiness check works](/php/how-to/deploy-process#readi #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your PHP application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -17789,86 +17055,50 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains PHP and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - The hostname of the new service will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [PHP service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -17884,7 +17114,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add PHP service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -18090,14 +17319,11 @@ The default PHP+Apache service has following Apache configuration: ServerName localhost DocumentRoot {{.DocumentRoot}} DirectoryIndex index.htm index.html index.shtml index.php index.phtml - Options -Indexes Options FollowSymLinks AllowOverride All Require all granted - SetHandler "proxy:unix:{{.PhpSocket}}|fcgi://localhost/" - ErrorLog "| /usr/bin/logger -tapache -plocal1.err" CustomLog "| /usr/bin/logger -tapache -plocal1.notice" combined ``` @@ -18205,7 +17431,6 @@ zerops: As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-php-hello-world), a **_recipe_**, containing the most simple PHP web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of PHP running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-php-hello-world/blob/main/import-project/description.yaml)): ```yaml @@ -18243,26 +17468,22 @@ Have you build something that others might find useful? Don't hesitate to share # Postgresql > Faq Question: How do I properly use PostgreSQL in HA mode? -Answer: +Answer: In High Availability (HA) mode, PostgreSQL runs on multiple containers with a primary node and replicas. To get the most out of this setup: - Use port `5432` for all write operations (INSERT, UPDATE, DELETE) — this always routes to the primary node - Use port `5433` for read operations (SELECT) — this distributes queries across all replicas, improving performance The read replica port (`5433`) is only available in HA mode. If you're running PostgreSQL in single container (NON_HA) mode, only port `5432` is available. See [Connection Parameters](/postgresql/how-to/connect#connection-parameters) for all available ports and environment variables. - Question: Why is my connection to PostgreSQL from third-party software failing? -Answer: - *One possible cause:* +Answer: + *One possible cause:* The connection string in Zerops always starts with `postgresql://`. While the official PostgreSQL documentation states that both `postgresql://` and `postgres://` URIs are valid, some software requires the shorter `postgres://` version. - To resolve this, create your own environment variable with the correct URI. For example, if your PostgreSQL service is named `db`, use the following format: - ``` postgres://${db_user}:${db_password}@${db_hostname}:${db_port} ``` - ---------------------------------------- @@ -18316,52 +17537,42 @@ Zerops provides several ways to connect to PostgreSQL: You'll find PostgreSQL connection details in the service detail page under the **Peek access details** button (shows hostname, port, user, password, and connection string). The full list of connection-related environment variables is available in the service detail under **Environment variables**. ### Connection Parameters - Parameter Internal External (TLS) Env Variable - Hostname Service hostname Public IP address `hostname` - Port (primary) 5432 6432 (via pgBouncer) `port` / `portTls` - Port (replicas, HA only) 5433 N/A `portReplicas` - User Identical to hostname Same as internal `user` - Password Generated at creation Same as internal `password` - Connection string (primary) `postgresql://${user}:${password}@${hostname}:5432/${dbName}` Same format with TLS port `connectionString` / `connectionTlsString` - Connection string (replicas, HA only) `postgresql://${user}:${password}@${hostname}:5433/${dbName}` N/A `connectionStringReplicas` - Database name db Same as internal `dbName` - :::tip If you're running PostgreSQL in High Availability (HA) mode, configure your application to route read queries to port **5433**. This distributes the load across all replicas, reducing pressure on the primary node and improving overall throughput. ::: @@ -18527,97 +17738,61 @@ The hostname of the first service will be set to `postgresql1`. The [high availa The hostname of the second service will be set to `postgresql2`. The [single container](/features/scaling#single-container-mode) mode will be chosen and the default [auto scaling configuration](/postgresql/how-to/scale) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in the `services:` section is required. You can create a project with multiple services. The example above contains only PostgreSQL services but you can create a `description.yaml` with [different types] of services. - Parameter Description - hostname - The unique service identifier. - - The hostname of the new database will be set to the `hostname` value. - + The hostname of the new database will be set to the `hostname` value. Limitations: - - duplicate services with the same name in the same project are forbidden - - maximum 25 characters - - must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [PostgreSQL service types](/references/import-yaml/type-list#database-services) are currently supported. - mode - Defines the operation mode of the PostgreSQL service. - HA - Creates a PostgreSQL cluster with 3 database containers and 2 free database proxies. This mode is suited for production. - Zerops always keeps the 3 database containers on different physical machines. All your data is stored redundantly in 3 identical copies. In case of a failure of a container or the underlying physical machine, Zerops automatically disconnects the failed container from the cluster, creates a new container and syncs all data from the remaining 2 copies. Finally, the broken container is automatically deleted. - In HA mode, a dedicated read replica port (5433) is available, allowing you to route read queries to replicas for better performance. See Connection Parameters for details. - NON_HA - Zerops will create a PostgreSQL database installed in a single container. Useful for non-essential data or dev environments. - Your data is stored only in a single container. If the container or the the underlying physical machine fails, your data since the last backup are lost. Zerops doesn't provide any automatic repairs of a single node PostgreSQL services. - verticalAutoscaling - Optional. Defines custom vertical auto-scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - :::caution The PostgreSQL service **hostname** and **mode** are fixed after the service is created. They can't be changed later. ::: @@ -18636,7 +17811,6 @@ Zerops will create a project and one or more services based on the `description. The maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add PostgreSQL service to an existing project #### Example Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -18831,7 +18005,6 @@ You can configure scaling settings: - **During import** - Define scaling configuration in your YAML import file using `verticalAutoscaling` parameters - **After service creation** - Navigate to your PostgreSQL service and select **Automatic scaling configuration** to modify settings ### Basic settings - **CPU Mode**: Choose between shared (cost-effective, variable performance) or dedicated (consistent performance, higher cost). You can change CPU mode once per hour. See [pricing](https://zerops.io/#pricing) for costs. **Resource limits**: Configure minimum and maximum resources for your PostgreSQL service: - **Lower the maximum** to control costs and prevent over-scaling @@ -18853,7 +18026,6 @@ When a container fails in HA mode, Zerops automatically replaces it with a new c - **Absolute (GB)**: Maintains this amount of free RAM at all times - **Percentage**: Keeps this percentage of total RAM free Consider increasing these values if your database experiences memory-related issues. - :::info Read More For detailed technical parameters and scaling behavior, see [Automatic Scaling and High Availability](/features/scaling#resource-scaling-behavior). ::: @@ -19241,19 +18413,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a Python service with hostname = "app" and port = 8000 from another service of the same project, simply use `app:8000`. Read more about [how to access a Python service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the Python runtime environment by installing additional dependencies or tools to the runtime base environment. The base Python environment contains {data.alpine.default}, the selected @@ -19379,25 +18546,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -19419,16 +18580,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Python application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -19471,25 +18628,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -19512,16 +18663,12 @@ Read more about how the [readiness check works](/python/how-to/deploy-process#re #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Python application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -19667,86 +18814,50 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Python and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - The hostname of the new service will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Python service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -19762,7 +18873,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Python service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -19870,7 +18980,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-python-hello-world), a **_recipe_**, containing the most simple Python web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Python running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-python-hello-world/blob/main/import-project/description.yaml)): ```yaml @@ -19965,7 +19074,7 @@ https://api.app-prg1.zerops.io/api/rest/public The API uses Bearer token authentication. You can obtain your Personal access token from the **Access Token management** section in the Zerops GUI. Include the token in the Authorization header: ``` -Authorization: Bearer +Authorization: Bearer ``` ## API Resources :::note @@ -19974,61 +19083,42 @@ Some resource groups have non-obvious naming: - `/user-data` endpoints handle environment variables management ::: View the [full Swagger documentation](https://api.app-prg1.zerops.io/api/rest/public/swagger) or jump to a specific resource group: - Group Description - /app-version Manage application versions, builds, and deployments - /auth Authentication and token management - /billing Billing operations and payment management - /client Client account management - /client-user User management within client accounts - /github GitHub repository connections and authorization - /gitlab GitLab repository connections and authorization - /project Project management operations - /project-env Project environment variables management - /public-http-routing HTTP routing configuration - /public-port-routing Port routing and firewall rules configuration - /service-stack Service stack operations and configuration - /settings System settings and configurations - /user User account management - /user-data Environment variables management - /user-notification User notifications management - /user-token Personal access tokens management - ---------------------------------------- @@ -20085,12 +19175,11 @@ You can find zCLI binary inside `result/bin`. Personal tokens allow you to securely access Zerops directly. You can create one or more personal tokens and manage them in [Zerops GUI](https://app.zerops.io). ### Managing your access tokens To create a new personal access token, go to [Access Tokens management](https://app.zerops.io/settings/token-management) and click "Generate a new access token". - You may delete an access token anytime. ### Using access tokens You may use an access token to access Zerops using this command: ```sh -zcli login +zcli login ``` ---------------------------------------- @@ -20127,7 +19216,6 @@ Zerops requires full access to your repository in order to configure webhooks an 4. **Finalize Setup** - Review and confirm your settings to complete the integration process. - Your GitHub repository is now connected, and builds will be triggered based on the configured triggers. - --- ### Skip the automatic pipeline once To ensure that a pipeline is not triggered by a specific commit, add `[ci skip]` or `[skip ci]` to the commit message. It is case insensitive. @@ -20141,7 +19229,6 @@ To disconnect your GitHub repository from Zerops: 1. Select **Build, Deploy, Run Pipeline Settings** from the menu. 1. Click **Stop automatic build trigger** **This action will remove the GitHub webhook and delete the associated integration configuration, effectively halting automated builds from this repository.** - ### Authorizing Access to Organizations If you want to authorize Zerops to access a specific organization, you can do so by clicking on the **Grant** from [GitHub Applications Settings](https://github.com/settings/connections/applications/4408deded6c1fed7193c). --- @@ -20152,20 +19239,16 @@ As an alternative to direct integration, you can use GitHub Actions to manage yo Create a new file at `.github/workflows/deploy.yaml` in your repository: ```yaml name: Deploy to Zerops - on: push: branches: - main - jobs: deploy: runs-on: ubuntu-latest - steps: - name: Checkout code uses: actions/checkout@v3 - - name: Deploy with Zerops uses: zeropsio/actions@main with: @@ -20234,7 +19317,6 @@ To disconnect your GitLab repository from Zerops: 1. Select **Build, Deploy, Run Pipeline Settings** from the menu. 1. Click **Stop automatic build trigger** **This action will remove the GitLab webhook and delete the associated integration configuration, effectively halting automated builds from this repository.** - ---------------------------------------- @@ -20256,7 +19338,6 @@ services: envSecrets: # yamlPreprocessor feat: generates a random 64 char and stores it SECRET_KEY: )> - - hostname: db type: postgresql@16 mode: HA @@ -20267,37 +19348,26 @@ After you've added the `yamlPreprocessor=on` line, the preprocessor will be enab The YAML script processing happens in two sequential steps. During the first turn (preprocessing), the system scans for Zerops import functions and modifiers, evaluates them, and replaces function calls with their results. All this data is stored in memory. The second turn handles standard processing, where the system creates the imported project, instantiates all required services, and handles environment variables, including setting up any new ones as needed. ### Syntax Reference - Sequence Description - ` The identifier of the beginning of a function expression syntax. - `(` The identifier of the beginning of the function parameters. - `)` The identifier of the end of the function parameters. - `,` A function parameters delimiter. - ` Identifier of the beginning of a string expression (without @). - `>` The identifier of the end of a string expression or a function expression syntax. - `|` The separator between a string or a function expression content and a modifier name. - `\` Escaping character. - `\<>|` Characters that need to be escaped to print them out. - :::note All functions in the preprocessor return values as strings, regardless of the operation performed. This includes numeric operations, random generation, and variable manipulation. ::: @@ -20309,11 +19379,11 @@ Functions generally support 2 types of parameters. - Spaces between `` are NOT trimmed. - When the value is passed as a function parameter, it's always a string. ```yaml -# String expression: +# String expression: SECRET_KEY: )> -# String expressions: and +# String expressions: and RANDOM_RANGE: , )> -# String expressions: , , +# String expressions: , , PICK_VALUE: , , )> ``` ### Variable reference names @@ -20326,9 +19396,9 @@ Variables are used to store and reuse values across your configuration. They can # Stores a value in myPassword variable PASSWORD: , )>)> # Retrieves the stored value (myPassword) -HASHED_PASSWORD: +HASHED_PASSWORD: # Reuses the stored value (myPassword) -SAME_PASSWORD_AGAIN: +SAME_PASSWORD_AGAIN: ``` :::tip Internal processing of function parameters All parameters are handled as strings internally. For functions that expect numbers (like `generateRandomString`), the string values are automatically converted to numbers during processing. @@ -20389,11 +19459,11 @@ It means that if `DIR: ` is used, the final value stored in the environment vari # Function will receive one parameter as the internal variable reference: commentValue # Its value is returned as a string if such an internal variable exists. If not, an error returns. # Examples of function expressions with escaped characters: -\ # Output: +\ # Output: \)\> # Output: )> \, \)\> # Output: , )> \\, \)\> # Output: , )> -\, )\> # Output: +\, )\> # Output: ``` --- :::tip What happens with environment variable references @@ -20415,19 +19485,14 @@ The `type: nodejs@latest` is just an example. The functions are available for al --- ### generateRandomString (length) This function generates a random string of specified length using alphanumeric characters and some special characters (`_`, `-`, `.`). It's useful for creating random passwords, API keys, or any other secret values that needs to be unique and unpredictable. - Field Details - Syntax: `)>` - Limitation: Length of the string should be an *integer* between `1` and `1024` characters. eg. ``, ``, ``. - Output: hR5hS79H4p - #### Usage in import YAML ```yaml #yamlPreprocessor=on @@ -20444,19 +19509,14 @@ RANDOM_STRING: 58580f0ad377a8e4c0dccc1622e2d3812b90 --- ### generateRandomBytes (length) This function generates requested amount of cryptographically random bytes. It is useful for creating random passwords, API keys, or any other secret values that needs to be unique and unpredictable. - Field Details - Syntax: `)>` - Limitation: Length of the string should be an *integer* between `1` and `1024` characters. eg. ``, ``, ``. - Output: hR5hS79H4p - #### Usage in import YAML ```yaml #yamlPreprocessor=on @@ -20473,22 +19533,16 @@ RANDOM_STRING: 58580f0ad377a8e4c0dccc1622e2d3812b90 --- ### generateRandomInt (min, max) This function generates a random integer within a specified range (inclusive). It's particularly useful when you need random numeric values, like ports, timeouts, or any other configuration that requires random numbers within specific bounds. - Field Details - Syntax: `, )>` - Minimum: Required minimum (inclusive) as a signed integer (from -4,611,686,018,427,387,903 to 4,611,686,018,427,387,903). - Maximum: Required maximum (inclusive) as a signed integer (from -4,611,686,018,427,387,903 to 4,611,686,018,427,387,903). - Output: 823 - #### Usage in import YAML ```yaml #yamlPreprocessor=on @@ -20504,19 +19558,14 @@ RANDOM_INT: 823 ``` --- ### pickRandom (...param) - Field Details - Syntax: `, , , , , )>` - Parameters: The required parameters from which the selection will be made. eg. ``, ``, ``, etc. - Output: 1024 - This function randomly selects one value from a list of provided options. It's helpful when you want to randomly assign values from a predefined set, such as screen resolutions, configuration options, or any other scenario where you need random selection from specific choices. #### Usage in import YAML ```yaml @@ -20534,22 +19583,16 @@ PICK_RANDOM: 1024 --- ### setVar (name, content) This function stores a value in memory for later use and returns the stored value. It's particularly useful when you need to reuse the same generated value multiple times in your configuration, ensuring consistency across different environment variables. - Field Details - Syntax: `, )>)>` - Variable: The required parameter of the internal variable name under which the provided content can be retrieved later using getVar function. The chosen name is case-sensitive. eg. ``, ``, etc - Content: Any text you want to be stored, including composition using functions. Already existing variable with the same name is overwritten. - Output: N4KdtM41WskS74wx - #### Usage in import YAML ```yaml #yamlPreprocessor=on @@ -20566,19 +19609,14 @@ SET_VAR: N4KdtM41WskS74wx --- ### getVar (name) This function retrieves a previously stored value from memory. It works in conjunction with setVar and other storage functions to access values that were generated or stored earlier in the configuration process. eg. `plainPassword` - Field Details - Syntax: `` - Variable: The required parameter of the internal variable name that is case-sensitive. The parameter is used as a reference, so it MUST NOT be enclosed in < and >. eg. `(plainPassword)`, `(varName)`, etc - Output: N4KdtM41WskS74wx - #### Usage in import YAML ```yaml #yamlPreprocessor=on @@ -20586,7 +19624,7 @@ services: - hostname: app type: nodejs@20 envSecrets: - GET_VAR: + GET_VAR: ``` #### Output of import YAML ```yaml @@ -20596,22 +19634,16 @@ GET_VAR: N4KdtM41WskS74wx ### generateRandomStringVar (name, length) This function combines the functionality of `generateRandomString` and `setVar`. It generates a random string and automatically stores it under a specified variable name for later use. The output contains only a random combination of alphanumeric characters (`a-zA-Z0-9`) and select special characters (`_`, `-`, `.`). - Field Details - Syntax: `, )>` - Variable: The required parameter of the internal variable name under which the provided content can be retrieved later using getVar function. The chosen name is case-sensitive. eg. ``, ``, etc - Limitation: Required length of the string to be generated in characters (maximum allowed value is 1024). - Output: hR5hS79H4p - #### Usage in import YAML ```yaml #yamlPreprocessor=on @@ -20628,24 +19660,18 @@ GENERATE_RANDOM_STRING_VAR: hR5hS79H4p --- ### generateJWT (tokenSecret, jsonPayload) This function generates a JWT (JSON Web Token) signed by HS256 algorithm using provided secret and payload. The payload MUST be a valid JSON value. Default values for `iss` and `iat` are set to `zerops` and current timestamp respectively. - Field Details - Syntax: `, )>` - Token Secret: The required secret (`string`) used to sign your JWT. - JSON Payload: The required payload part of the JWT (`string`) in JSON format. - Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3Mjkw Njk4NTcsImlzcyI6Inplcm9wcyJ9.xPrqHDtGhK5c7WMJliguwBeKI29qzAoD7KXrtACb jio - #### Usage in import YAML ```yaml #yamlPreprocessor=on @@ -20655,13 +19681,10 @@ services: envSecrets: # Using a fixed secret string JWT_FIXED: , )> - # Using an empty payload JWT_EMPTY: , )> - # Generate and store a random secret, then use it JWT_RANDOM: , )>, )> - # Use a previously stored secret JWT_STORED: , )> ``` @@ -20676,27 +19699,20 @@ JWT_STORED: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4iLCJleHAiOjE ### getDateTime (format, [timezone]) This function returns the current date and time formatted according to your specifications. It's useful for timestamps, logging, and any scenario where you need the current time in a specific format and timezone. Returns the current date and time in a specified mask pattern and a chosen timezone. - Field Details - Syntax: `, )>` - Mask: The required parameter of the chosen timezone. eg. `` - Timezone: - The optional parameter of the chosen timezone. It's possible to select from three different formats: Abbreviation, Location, and POSIX. If not specified, the GMT timezone is set by default. You can see the complete listing of all possible values. POSIX format uses the opposite sign. Times to the west are positive, to the east negative. For Central European Time, you can use `CET`, `Europe/Prague`, `Etc/GMT-2` (summer), `Etc/GMT-1` (winter). Abbreviated and location formats return values with the currently valid DST. eg. ``, ``, `` - Output: 11.12.2022 18:06:13 - #### Usage in import YAML ```yaml #yamlPreprocessor=on @@ -20711,7 +19727,6 @@ services: GET_DATETIME: 11.12.2022 18:06:13 ``` #### Masking - When using the `getDateTime` function, you can customize the output format using various masks. Here's a comprehensive list of available format patterns: #### Year - `YYYY` - Full year (e.g., 2024) @@ -20753,54 +19768,42 @@ GET_DATETIME: 11.12.2022 18:06:13 TIMESTAMP: , )> FRIENDLY_DATE: , )> ``` - --- :::tip Example Usage for Environment Variables - For Multiline Generation use: ```yaml APP_PUBLIC_KEY: | - ``` - For Single Value Generation use: ```yaml -APP_PUBLIC_KEY_SSH: +APP_PUBLIC_KEY_SSH: ``` ::: --- ### generateED25519Key (name) Generates public and private ED25519 key pairs (including SSH) and stores them for later use as internal variables with names using the base name and variants. - Field Details - Syntax: `)>` - Variants: The base name parameter stores all generated key versions as internal variables, combined with the Available Variants. - #### Available Variants - Variant Description Retrieving the value - Public Public key version. This value is also returned by the called function. `` - PublicSsh SSH formatted public key version. For use as the authorized key file. `` - Private Private key version in the standard format. Not usable for OpenSSH. `` - PrivateSsh Private key version in the OpenSSH format. `` - #### Usage in import YAML :::caution These Generated keys are formatted as multiline strings. That means using the `|` syntax is necessary in import YAML. @@ -20814,18 +19817,14 @@ services: GENERATED_PUBLIC_KEY: | )> APP_PUBLIC_KEY: | - - APP_PUBLIC_KEY_SSH: + APP_PUBLIC_KEY_SSH: APP_PRIVATE_KEY: | - APP_PRIVATE_KEY_SSH: | - ``` #### Output of import YAML :::info You can clearly see the multiline strings and the `|` syntax in Output. ::: - - Generated as a multiline value - The same value as in APP_PUBLIC_KEY. ```yaml @@ -20834,7 +19833,6 @@ services: MCowBQYDK2VwAyEAu0/gEIpNUbVdqA20lVl+ZD+5/zzfVK4exrgGLxgQQRU= -----END PUBLIC KEY----- ``` - - Generated as a multiline value. - The same value as in GENERATED_PUBLIC_KEY. ```yaml @@ -20843,12 +19841,10 @@ services: MCowBQYDK2VwAyEAu0/gEIpNUbVdqA20lVl+ZD+5/zzfVK4exrgGLxgQQRU= -----END PUBLIC KEY----- ``` - - Generated as a single value ```yaml APP_PUBLIC_KEY_SSH: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILtP4BCKTVG1XagNtJVZfmQ/uf8831SuHsa4Bi8YEEEV ``` - - Generated as a multiline value ```yaml APP_PRIVATE_KEY: | @@ -20856,7 +19852,6 @@ services: MC4CAQAwBQYDK2VwBCIEIFzW6uPLlMnse2Hqg4hlyTwtlWaPKHjyoaaBi/FfVxBQ -----END PRIVATE KEY----- ``` - - Generated as a multiline value ```yaml APP_PRIVATE_KEY_SSH: | @@ -20868,38 +19863,28 @@ services: ULtP4BCKTVG1XagNtJVZfmQ/uf8831SuHsa4Bi8YEEEVAAAAAAECAwQF -----END OPENSSH PRIVATE KEY----- ``` - --- ### generateRSA2048Key (name) This Generates public and private RSA 2048bit key pairs (including SSH) and stores them for later use as internal variables with names using the base name and variants. - Field Details - Syntax: `)>` - Variants: The base name parameter stores all generated key versions as internal variables, combined with the Available Variants. - #### Available Variants - Variant Description How to use with: `)>` - Public Public key version. This value is also returned by the called function. `` - PublicSsh SSH formatted public key version. For use as the authorized key file. `` - Private Private key version in the standard format. `` - #### Usage in import YAML :::caution These Generated keys are formatted as multiline strings. That means using the `|` syntax is necessary in import YAML. @@ -20914,16 +19899,13 @@ services: GENERATED_PUBLIC_KEY: | )> APP_PUBLIC_KEY: | - - APP_PUBLIC_KEY_SSH: + APP_PUBLIC_KEY_SSH: APP_PRIVATE_KEY: | - ``` #### Output of import YAML :::info You can clearly see the multiline strings and the `|` syntax in Output. ::: - - Generated as a multiline value - The same value as in APP_PUBLIC_KEY. ```yaml @@ -20938,7 +19920,6 @@ services: UwIDAQAB -----END PUBLIC KEY----- ``` - - Generated as a multiline value. - The same value as in GENERATED_PUBLIC_KEY. ```yaml @@ -20953,12 +19934,10 @@ services: UwIDAQAB -----END PUBLIC KEY----- ``` - - Generated as a single value ```yaml APP_PUBLIC_KEY_SSH: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJYwrH690Rv9bDX22tX0OiDvHp3gwIvi7nvp9V0OMEWFeNdisrRjSai+9g17vaq2mCBbwWcCIpsU1YwuvuZXXa7QkR0/NqpI5f9cpEpararvDJmMZTS5UsndF3ZjjIXKODrNu8DQeHkdGBufvGhExRtPea3WUE/4N4ciL17FO+2I1+k7RnVuJ2Ysu/MudqSDV6LxhacQAUmLzf8y5EyEhY4DfaHXhZVu+DhIDv75JMcXe4cv6Qcns/8p6oAIME9uBxz1LPtaxJMA7o92vLeysoI/rQAxpPY97AHnUC268VHyc/TlayQyB3zzsoddmOT02ENTkTTq/Pid/tre36K3RT ``` - - Generated as a multiline value ```yaml APP_PRIVATE_KEY: | @@ -21015,38 +19994,28 @@ services: A0bX/JM8kHjLlJNrtioxcT+dX4lL6/zT -----END PRIVATE KEY----- ``` - --- ### generateRSA4096Key (name) This Function Generates public and private RSA 4096bit key pairs (including SSH) and stores them for later use as internal variables with names using the base name and variants. - Field Details - Syntax: `)>` - Variants: The base name parameter stores all generated key versions as internal variables, combined with the Available Variants. - #### Available Variants - Variant Description How to use with: `)>` - Public Public key version. This value is also returned by the called function. `` - PublicSsh SSH formatted public key version. For use as the authorized key file. `` - Private Private key version in the standard format. `` - #### Usage in import YAML :::caution These Generated keys are formatted as multiline strings. That means using the `|` syntax is necessary in import YAML. @@ -21060,16 +20029,13 @@ services: GENERATED_PUBLIC_KEY: | )> APP_PUBLIC_KEY: | - - APP_PUBLIC_KEY_SSH: + APP_PUBLIC_KEY_SSH: APP_PRIVATE_KEY: | - ``` #### Output of import YAML :::info You can clearly see the multiline strings and the `|` syntax in Output. ::: - - Generated as a multiline value - The same value as in APP_PUBLIC_KEY. ```yaml @@ -21089,7 +20055,6 @@ services: LdzErIs9vEaox5Qe6l4lhAUCAwEAAQ== -----END PUBLIC KEY----- ``` - - Generated as a multiline value. - The same value as in GENERATED_PUBLIC_KEY. ```yaml @@ -21109,12 +20074,10 @@ services: LdzErIs9vEaox5Qe6l4lhAUCAwEAAQ== -----END PUBLIC KEY----- ``` - - Generated as a single value ```yaml APP_PUBLIC_KEY_SSH: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDWRgQntuMGJfMEwj9wLrBqNyUd13k1nRuzHZ3x5VJodRrPjX19M9gFXuY95zTJti6VOG+pZftzuTkf+MlW9NwFEe/g0OomY6UwfKfPj4/ib3MPiASg2o4Ixqu0mv3IrLOsGmU25J38gAVQI3oohW4+B7Vp+2+RnLQCx1FsweWpa8wR0ffQwl0LsWSEWyutfSxi+5pbYwWORBOKyESmGFqGBhMfl0KutBqNAGLt9IYS61bYIqnvfzHqE1uIH3/+ViNzMAr56xt8Lr5a+Y84Mmer1h1wnh6OHnOE6y2sw+876RO8OjMTnHq6v1HDnKHQyCNHxDvpqihy7hlyRtCpC9fuCJC94lB0Gf65xgJC55Jx7gGRSbdLUhN2XZdQeAGPtdidz5KqC8S02fhiDKe0C78hpUGD8MZU4GqvqlyiHoouBhKugkba7F0NeSOLVC9GPLdKNjt5AyCi72pQz8vXaBk8TIb7F+WeQ3NtLw8sgZj0XjXRtx/S6SSoTIOkIKB6hKsPV0k+Z7VaVJ+FpXPsr2CbwcH0iyCJC6A6hLQWnDnts26PLcFck7hE6UjE4BsBhXQhcyMTe5Yai3y4V5NWoHinkLO9NX2N2hBcpQWSSCbr0wSg33xAHROzcG1w5/n1fq46723CbWW3GcsfnaL/hPIt3MSsiz28RqjHlB7qXiWEBQ== ``` - - Generated as a multiline value ```yaml APP_PRIVATE_KEY: | @@ -21171,52 +20134,36 @@ services: A0bX/JM8kHjLlJNrtioxcT+dX4lL6/zT -----END PRIVATE KEY----- ``` - ## Import modifiers Modifiers provide a simpler way to transform values compared to using functions alone. You can chain multiple modifiers together using the `|` symbol, making it easy to apply several transformations in sequence. They work with both string and function expressions - just add them between the `` markers, right before the closing `>`. --- ### List of import modifiers - Name Description - sha256 Generate a hash of the incoming string using sha256 algorithm. - sha512 Generate a hash of the incoming string using sha512 algorithm. - bcrypt - Generate a hash of the incoming string using bcrypt algorithm. Fixed configuration: Number of cycles = 11 - argon2id - Generate a hash of the incoming string using argon2id algorithm. Fixed configuration: Memory = 64MiB, Iterations = 4, Parallelism = 4, SaltLen = 16B, KeyLength = 32B - toHex Encodes provided string/bytes into hexadecimal - toString Encodes provided string/bytes into string comprised of [a-zA-Z0-9_-.] - upper Maps all unicode letters to their upper case - lower Maps all unicode letters to their lower case - title Maps all words to title case (first letter upper case, rest lower case) - noop Does nothing - used in tests - --- ### Examples of correctly using import modifiers - - `, )>` will output a random string of 30 characters: ```yaml 7a14c8e74bc98a0d74253b1d1a4ef6 @@ -21237,32 +20184,30 @@ Modifiers provide a simpler way to transform values compared to using functions ```yaml $argon2id$v=19$m=98304,t=1,p=3$uWBpmoUT3sfckXHyRF9hlg$8bGtNffuHxaRIgN99zCmJeGEYJF5BY2J9TwzqmezP28 ``` - - Using upper case modifier: ```yaml - Input: + Input: Output: STATIC STRING WITH A MODIFIER ``` - Using lower case modifier: ```yaml - Input: + Input: Output: static string with a modifier ``` - Using title case modifier: ```yaml - Input: + Input: Output: Static String With A Modifier ``` - Chaining multiple modifiers: ```yaml - Input: + Input: Output: static string with a modifier ``` - Using modifiers with functions: ```yaml Input: ) | upper> Output: 7A14C8E74BC98A0D74253B1D1A4EF6 - Input: , ) | lower>)> Output: h73ep149sd ``` @@ -21280,96 +20225,54 @@ Versions listed on the same line are aliases of the same underlying version. ::: ## Available service types ### Static Services - Service Type Versions - Nginx - Static - ### Containers and virtual machines - Service Type Versions - Alpine - Ubuntu - ### Runtime services - Service Type Versions - Bun - Deno - .NET - Elixir - Gleam - Go - Java - Node.js - PHP & Apache - PHP & nginx - Python - Rust - ### Database services - Database Type Versions - KeyDB - MariaDB - PostgreSQL - Qdrant - Valkey - ### Search Engine - Search Engine Versions - Elasticsearch - Meilisearch - Typesense - ### Message Broker - Message Broker Versions - Kafka - NATS - ### Storage Services - Database Type Versions - Object storage - Shared storage - ---------------------------------------- @@ -21493,38 +20396,30 @@ The example above is a general guideline; not all keys are valid for every servi ## Project Configuration The project configuration is used to define the project you want to import. ### Usage - Field Type Description - project object - _REQUIRED, if a whole project is imported_ + _REQUIRED, if a whole project is imported_ Only one project can be defined. - name string, REQUIRED The name of the new project. Duplicates are allowed. - description string Description of the new project. - corePackage string - [Core package](/features/infrastructure#project-core-options) of the new project. + [Core package](/features/infrastructure#project-core-options) of the new project. Values: LIGHT/SERIOUS (default LIGHT) - tags list of strings One or more string tags. Tags provide better orientation in projects. - envVariables map[string]string [Project-level environment variables](/features/env-variables#project-variables) that are available to all services in the project. - :::important The `corePackage` value can be upgraded later from Lightweight to Serious Core, but cannot be downgraded. Upgrades involve a brief service disruption and are partially destructive (logs/statistics are lost). Make sure to choose a suitable core package for your project. Learn more about [core upgrade process](/features/infrastructure#project-core-upgrade). ::: @@ -21643,79 +20538,57 @@ services: ``` This example includes all possible configuration options for Zerops services. Not all options are required or applicable to every service type. The example shows two services in the same YAML file: a fully configured Node.js API service and a simpler static frontend service. ### Service Basic Configuration - Field Type Description - services list of objects, REQUIRED At least one service is required. - hostname string, REQUIRED - The unique service identifier. Limitations: - duplicates in the same project forbidden - maximum 25 characters, lowercase ASCII letters (a-z) or numbers (0-9) only - type enum, REQUIRED Specifies the service type and version. See [supported types](/references/import-yaml/type-list). - mode enum Values: HA / NON_HA (default NON_HA) Defines the operation mode of the service. - envSecrets map[string]string Environment variables that are blurred by default in Zerops GUI. Can be edited or deleted in Zerops GUI. - dotEnvSecrets string (multiline) Environment variables in .env file format that are automatically created as secret envs. - objectStorageSize integer Object storage size in GB. - objectStoragePolicy enum - Values: **private / public-read / public-objects-read / public-write / public-read-write / custom** Select a predefined AWS S3 bucket access policy. - objectStorageRawPolicy json - Define your own AWS S3 bucket access policy. See [AWS docs](https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-policy-language-overview.html) for details. Use `{{ .BucketName }}` placeholder if you need to use bucket name in your custom policy rules. - buildFromGit string (URL) - A URL of a Github or Gitlab repository used for a one-time build of your service. - enableSubdomainAccess boolean - Default: `false` Set `true`, if you want to enable a public access to your service via a Zerops subdomain. Not suitable for production. - priority integer - Services are sorted before creation by priority in descending order, i.e. the higher the priority the sooner the service is created. - override boolean - Default: `false` This only works for **runtime** services. The parameter allows you to replace an existing runtime service with the same hostname byt triggering a redeploy if the service already exists. - ```yaml #yamlPreprocessor=on services: @@ -21761,68 +20634,45 @@ The `yamlPreprocessor` option in your project & service import YAML is required ::: ### Service Vertical Autoscaling The vertical autoscaling configuration defines how the service can scale its resources vertically. - Field Type Description - minCpu integer Minimum number of virtual CPUs - maxCpu integer Maximum number of virtual CPUs - cpuMode enum Values: **SHARED / DEDICATED** - minRam float - Minimum RAM in GB that each container of the service can scale down to. - maxRam float - Maximum RAM in GB that each container of the service can scale up to. - minDisk float - Minimum disk space in GB that each container of the service can scale down to. - maxDisk float - Maximum disk space in GB that each container of the service can scale up to. - startCpuCoreCount integer - Number of CPU cores with which each container starts. - minFreeCpuCores float - Minimum number of unused CPU cores before a container starts scaling. - minFreeCpuPercent float - Minimum percentage of unused CPU cores before a container starts scaling. - minFreeRamGB float - Minimum unused memory in GB before a container starts scaling. - minFreeRamPercent float - Minimum percentage of unused memory before a container starts scaling. - ```yaml services: - hostname: app @@ -21849,11 +20699,9 @@ This yaml will create a service with the hostname `app` with `php-nginx@8.4` run - Disk Space: `1-10 GB` ### Service Horizontal Autoscaling The horizontal autoscaling configuration is used to define the horizontal autoscaling settings for the service. - Field Type Description - minContainers integer Minimum number of containers of the service. @@ -21876,15 +20724,12 @@ services: The `minContainers` and `maxContainers` parameters allow you to define the minimum and maximum number of containers for the service. The service will automatically scale between these values as needed. ### Service Mount Shared Storage The mount shared storage configuration defines which shared storage services should be mounted to the service. - Field Type Description - mount list of strings Mount shared storage to the service. `buildFromGit` must be filled. - ```yaml services: - hostname: app @@ -21897,15 +20742,12 @@ services: The `mount:` parameter allows you to mount a shared storage (which should be created inside the project) to the service. ### Service Nginx Configuration The nginx configuration defines the nginx settings for the service. - Field Type Description - nginxConfig string (multiline) Insert full nginx config. - ```yaml #yamlPreprocessor=on services: @@ -21928,19 +20770,15 @@ services: The `nginxConfig: |-` parameter allows you to specify a custom nginx configuration for the service. ### Service zerops.yaml Configuration The `zeropsSetup` and `zeropsYaml` parameters provide flexibility in how you define and use your service configurations. Both parameters are optional and work together in the following ways: - Field Type Description - zeropsSetup string Specifies which service setup to use. This should match a setup name found in either the `zeropsYaml` parameter (if provided) or the `zerops.yaml` file in the repository root. If not specified, defaults to the service hostname. - zeropsYaml object Contains the full [zerops.yaml configuration](/zerops-yaml/specification). If provided, this will be used instead of looking for a `zerops.yaml` file in the repository. - ```yaml services: - hostname: app @@ -22324,11 +21162,11 @@ This rule allows certificate validation to work while maintaining HTTPS enforcem ### DNS Resolution Testing ```bash # Check IPv4 resolution -dig A +dig A # Check IPv6 resolution -dig AAAA +dig AAAA # Check from specific DNS server -dig @1.1.1.1 +dig @1.1.1.1 ``` ### Connectivity Testing ```bash @@ -22565,7 +21403,7 @@ To use both `` and `*.`, specify both variants in your [Zerops configuration](/r Test your configuration: ```bash # Check DNS resolution -dig AAAA +dig AAAA # Verify connectivity curl -vI https:// # Test IPv4 access @@ -22724,7 +21562,7 @@ For complete VPN setup, configuration, and troubleshooting, see the [VPN Referen Use [SSH](/references/networking/ssh) to connect to your service for debugging and system administration. ```bash # Connect to a specific service -ssh +ssh ``` **Important:** SSH access is temporary and changes are not persistent across deployments. :::tip SSH Configuration @@ -22760,32 +21598,26 @@ The L7 HTTP Balancer handles all HTTP/HTTPS traffic and provides advanced applic Access the advanced balancer configuration through your project's HTTP Balancer section → **Advanced balancer configuration**. ### Connection Handling Configure how the balancer manages client connections: - Setting Default Range Parameter - Maximum simultaneous connections per worker 4000 1024-65535 worker_connections - Accept multiple connections at once on on/off multi_accept - How long to keep idle connections open 30s 1s-300s keepalive_timeout - Maximum number of requests per connection 100000 1-1000000 keepalive_requests - :::tip Recommendations - **High-traffic websites**: Increase `worker_connections` to 8000 or higher - **API services**: Adjust `keepalive_timeout` to 60 for longer connections @@ -22793,37 +21625,30 @@ Configure how the balancer manages client connections: ::: ### Client Request Settings Control how the balancer handles incoming requests: - Setting Default Range Parameter - Timeout for receiving client request header 10s 1s-300s client_header_timeout - Timeout for receiving client request body 10s 1s-300s client_body_timeout - Maximum allowed size of client request body 512m 1k-2048m client_max_body_size - Reset connections that have timed out on on/off reset_timedout_connection - Timeout for transmitting response to client 2s 1s-300s send_timeout - :::tip Recommendations - **File upload services**: Increase `client_body_timeout` and `client_max_body_size` to accommodate large files - **Slow clients**: Increase header and body timeouts @@ -22831,32 +21656,26 @@ Control how the balancer handles incoming requests: ::: ### Buffer Settings Optimize memory usage for request and response handling: - Setting Default Range Parameter - Size of buffer for client request header 1k 1k-64k client_header_buffer_size - Number of buffers for large client headers 4 1-16 large_client_header_buffers_number - Size of buffers for large client headers 8k 1k-64k large_client_header_buffers_size - Size of buffer for client request body 16k 1k-1m client_body_buffer_size - :::tip Recommendations - **Large headers**: Increase header buffer sizes for applications with extensive headers - **File uploads**: Optimize `client_body_buffer_size` based on typical upload sizes @@ -22864,42 +21683,34 @@ Optimize memory usage for request and response handling: ::: ### Proxy Settings Configure how the balancer communicates with backend services: - Setting Parameter Default Range - Enable buffering of client request body proxy_request_buffering off on/off - Enable buffering of responses from proxied server proxy_buffering on on/off - Size of the buffer used for reading the first part of the response proxy_buffer_size 32k 1k-256k - Number of buffers used for reading a response from the proxied server proxy_buffers_number 4 1-16 - Size of buffers for reading a response from the proxied server proxy_buffers_size 256k 1k-1m - Size of buffers that can be busy sending response to the client proxy_busy_buffers_size 256k 1k-1m - :::tip Recommendations - **Real-time APIs**: Set `proxy_buffering` to off for lower latency - **Large responses**: Increase `proxy_buffer_size` for handling larger API responses @@ -22907,37 +21718,30 @@ Configure how the balancer communicates with backend services: ::: ### Performance Optimization Enable various performance enhancements: - Setting Default Range Parameter - Use sendfile() for file transfers on on/off sendfile - Enable TCP_NOPUSH socket option on on/off tcp_nopush - Enable TCP_NODELAY socket option on on/off tcp_nodelay - Enable gzip compression on on/off gzip - Rate limit for response transmission (0 = no limit) 0 0-1000m limit_rate - :::tip Recommendations - **File serving**: Ensure `sendfile` and `tcp_nopush` are enabled for static content - **Real-time applications**: Verify `tcp_nodelay` is enabled @@ -22946,42 +21750,34 @@ Enable various performance enhancements: ::: ### File Cache Settings Optimize file system operations: - Setting Default Range Parameter - Cache open file descriptors on on/off open_file_cache - Maximum number of elements in file cache 200000 1000-1000000 open_file_cache_max - Time after which unused cache elements are removed 20s 1s-300s open_file_cache_inactive - Time interval for checking cached elements validity 30s 1s-300s open_file_cache_valid - Minimum file uses to remain in cache 2 1-100 open_file_cache_min_uses - Cache file lookup errors on on/off open_file_cache_errors - :::tip Recommendations - **Static file serving**: Increase cache size and adjust timeouts - **Development**: Reduce validation timeout for faster file updates @@ -22989,15 +21785,12 @@ Optimize file system operations: ::: ### Security Settings Configure security-related options: - Setting Default Parameter - Emit nginx version in error messages and headers off server_tokens - **Best Practice:** Keep `server_tokens` disabled to avoid revealing server information. ## Advanced Routing Features The L7 HTTP Balancer supports sophisticated routing beyond basic domain mapping. @@ -23009,7 +21802,6 @@ Redirect requests to different URLs with full control: - **Redirect Code**: HTTP status code for redirection (e.g., 301, 302, 307, 308) - **Preserve Path**: Keep original path in redirect URL - **Preserve Query**: Keep original query parameters in redirect URL - ### Access Policy Configuration Implement IP-based access control. If the request fails the check, a 403 Forbidden error is returned: **Policy Types:** @@ -23020,7 +21812,6 @@ Implement IP-based access control. If the request fails the check, a 403 Forbidd - IPv4 range: `192.168.1.0/24` - IPv6 address: `2001:db8::1` - IPv6 range: `2001:db8::/32` - ### Rate Limiting Configuration Protect against abuse and ensure fair resource usage. When the rate limit is exceeded, requests are delayed (burst). If they cannot be processed in time, a 503 Service Temporarily Unavailable error is returned: **Configuration Parameters:** @@ -23029,20 +21820,17 @@ Protect against abuse and ensure fair resource usage. When the rate limit is exc - **Burst**: Number of requests to queue when rate exceeded - **Zone Name**: Memory zone for storing rate limiting state - **Zone Size**: Memory allocated for rate limiting data (in MB) - ### Basic Authentication Add HTTP Basic Authentication to protected resources: **Configuration:** - **Realm**: Authentication realm name - **Users**: Username and password combinations - ### Custom Content Responses Return custom content for specific conditions: **Configuration:** - **HTTP Status Code**: Any valid status code (200, 404, 503, etc.) - **Content**: Response body content - **Content Type**: MIME type (default: text/plain) - *Need help? Join our [Discord community](https://discord.gg/zeropsio).* ---------------------------------------- @@ -23061,7 +21849,6 @@ Zerops subdomains provide quick public access through `.zerops.app` addresses, i 1. Navigate to your service detail page in Zerops GUI 2. Select **Subdomain & domain & IP access** from the left menu (for runtime services) 3. Toggle the **Zerops subdomain access** switch - Once enabled, Zerops assigns a unique subdomain for your application. If you've defined multiple [internal ports](/zerops-yaml/specification#ports-) with HTTP support in your `zerops.yaml`, each port receives its own unique `.zerops.app` subdomain. ### Technical Implementation When using Zerops subdomains: @@ -23079,7 +21866,6 @@ When using Zerops subdomains: ::: ## Custom Domain Access Custom domain access provides production-ready public access through your own domain names, offering better performance and full control over domain settings. - ### IP Address Configuration Before setting up domain access, you need to configure public IP addresses. Zerops offers the following options: #### IPv4 Configuration @@ -23158,7 +21944,6 @@ Direct port access enables public access to specific ports on your services, sup :::important Service Compatibility Currently, direct public port access is only available for runtime services and PostgreSQL databases. ::: - ### Port Configuration 1. Navigate to your service detail page in Zerops GUI: - For runtime services: Select **Subdomain & domain & IP access** @@ -23183,7 +21968,6 @@ Secure your public ports with optional firewall rules: 3. **Configure IP rules:** - **Single IP format:** Affects only the specific IP address - **IP range format:** Affects all IPs in the specified CIDR range - For information about Zerops' platform-wide firewall and port restrictions, see the [Firewall Reference Guide](/references/networking/firewall). ### Protocol Support Direct port access supports: @@ -23231,7 +22015,7 @@ The [Zerops CLI (zCLI)](/references/cli) comes bundled with the [Zerops VPN](/re ### 2. Establish SSH Connection via VPN Once your VPN session is active, you can connect to any [service](/features/infrastructure#services) using its hostname via SSH: ```sh -ssh +ssh ``` Example: ```sh @@ -23248,7 +22032,7 @@ Type `yes` to trust the host and prevent future prompts. #### Connecting to Specific Containers To access a specific container instead of the service as a whole, use the container's hostname: ```sh -ssh +ssh ``` Example: ```sh @@ -23509,23 +22293,18 @@ Simple Mail Transfer Protocol (SMTP) is the internet standard for email transmis ## SMTP in Zerops Zerops implements a security-first approach to email sending operations, allowing only port 587 for SMTP communication. This decision aligns with modern security practices and helps maintain the platform's integrity. ### Port Configuration - Port Status Description - 587 ✅ Allowed Modern SMTP submission with STARTTLS - 25 ❌ Blocked Traditional SMTP (security risk) - 465 ❌ Blocked Legacy SMTPS (deprecated) - ### Why Port 587 Port 587 is the modern standard for sending emails through SMTP, designed specifically for authenticated client submissions. This port enforces several security measures: - Mandatory TLS encryption for data protection @@ -23574,48 +22353,41 @@ These examples serve as a starting point. Check your email provider's official d Port 587 is mandatory for all SMTP configurations in Zerops. Other ports (25, 465) are blocked for security reasons. ::: #### Enterprise Email Providers - Service Host Port Secure Username Password - Gmail smtp.gmail.com 587 false your.name@gmail.com App Password required - Google Workspace smtp-relay.gmail.com 587 false your.name@your-domain.com Regular password (App Password if using 2FA) - Office 365 smtp.office365.com 587 false your.name@your-domain.com Account password - :::note Google - Gmail/Office 365: Better for testing or low-volume sending - Google Workspace: Suitable for business needs with higher limits ::: #### Email Service Providers - Provider Host Port Username Password Features - SendGrid smtp.sendgrid.net 587 @@ -23625,7 +22397,6 @@ Port 587 is mandatory for all SMTP configurations in Zerops. Other ports (25, 46 • Real-time analytics • Webhooks • Spam detection - Mailgun smtp.mailgun.org 587 @@ -23635,7 +22406,6 @@ Port 587 is mandatory for all SMTP configurations in Zerops. Other ports (25, 46 • Email validation • Routing rules • Delivery analytics - Amazon SES email-smtp.us-east-1.amazonaws.com 587 @@ -23645,7 +22415,6 @@ Port 587 is mandatory for all SMTP configurations in Zerops. Other ports (25, 46 • AWS integration • High deliverability • Auto-scaling - ## Best Practices When implementing email functionality in your applications: - Store SMTP credentials in environment variables @@ -23677,7 +22446,7 @@ For detailed information about configuration options, environment variables, and ### login Logs you into Zerops using a generated token or your login credentials. ```sh -zcli login +zcli login ``` ### logout Disconnects from VPN and logs out from your Zerops account. @@ -24029,7 +22798,7 @@ zsc completion [command] ### backup-create Creates a backup of any specified stack in your project. ```sh -zsc backup-create +zsc backup-create ``` #### Required parameters - ``: Name of the stack to backup @@ -24201,7 +22970,7 @@ All sub-commands share these parameters: - `--no-fail`: Prevents command failure with exit code 1 (default: false) **list** - Lists all available backups in the specified storage ```sh -zsc object-storage list --storage --name +zsc object-storage list --storage --name ``` **backup** - Creates a backup of specified directories to object storage ```sh @@ -24316,7 +23085,7 @@ The container will scale down automatically if resources are not utilized, or if ### setSecretEnv Securely update environment variables containing sensitive information. ```sh -zsc setSecretEnv +zsc setSecretEnv ``` #### Arguments - ``: The name of the environment variable to set @@ -24352,11 +23121,11 @@ zsc shared-storage [command] # View shared-storage help zsc shared-storage --help # Mount a shared storage volume -zsc shared-storage mount +zsc shared-storage mount # Unmount a shared storage volume -zsc shared-storage unmount +zsc shared-storage unmount # Wait for a storage mount to be ready -zsc shared-storage wait +zsc shared-storage wait ``` --- ### test @@ -24756,19 +23525,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a Rust service with hostname = "app" and port = 8080 from another service of the same project, simply use `app:8080`. Read more about [how to access a Rust service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the Rust runtime environment by installing additional dependencies or tools to the runtime base environment. The base Rust environment contains {data.alpine.default}, the selected @@ -24804,7 +23568,6 @@ Some packages or tools can take a long time to install. Therefore, Zerops caches 1. Content of the [build.addToRunPrepare](#copy-folders-or-files-from-your-build-container) and `run.prepareCommands` attributes didn't change from the previous deploy 2. The custom runtime cache wasn't invalidated in the Zerops GUI. To invalidate the Zerops runtime cache go to your service detail in Zerops GUI, choose **Service dashboard & runtime containers** from the left menu and click on the **Open pipeline detail** button. Then click on the **Clear runtime prepare cache** button. - When the prepare cache is used, Zerops doesn't create a prepare runtime container and executes the deployment of your application directly. #### Single or separated shell instances You can configure your prepare commands to be run in a single shell instance or multiple shell instances. The format is identical to [build commands](#buildcommands). @@ -24896,25 +23659,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -24936,16 +23693,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Rust application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -24988,25 +23741,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -25029,16 +23776,12 @@ Read more about how the [readiness check works](/rust/how-to/deploy-process#read #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Rust application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -25184,84 +23927,51 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Rust and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Rust service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -25277,7 +23987,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Rust service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -25385,7 +24094,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-rust-hello-world), a **_recipe_**, containing the most simple Rust web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Rust running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-rust-hello-world/blob/main/import-project/description.yaml)): ```yaml @@ -25454,7 +24162,6 @@ This page covers how to connect an existing shared storage to runtime services a When creating a new shared storage service, you can directly select which runtime services it should be connected to. See [Create Shared Storage](/shared-storage/how-to/create) for details about the creation process. ### Connect an existing shared storage For existing storage, go to the shared storage service detail page and select **Shared storage connections**. Toggle ON any runtime services you wish to connect to this storage. - ## Disconnect a shared storage in Zerops GUI To disconnect storage, access the shared storage service detail page, select **Shared storage connections**, and toggle OFF the desired runtime service. @@ -25475,7 +24182,6 @@ The hostname is fixed after the service is created. It can't be changed later. ::: ### Connect to Services Select one or more project's runtime services in the Share with Services block: - The new Shared Storage will be connected to the selected runtimes. :::note Runtime services can be connected and disconnected at any time even after the shared storage is created. @@ -25488,7 +24194,6 @@ See [Technical Details](/shared-storage/tech-details#deployment-modes) for more ::: ### Set Auto Scaling Configuration Configure vertical auto scaling parameters to control resource allocation and costs. - :::note For detailed information about auto scaling capabilities and recommendations, see [Technical Details](/shared-storage/tech-details#auto-scaling-configuration). ::: @@ -25548,7 +24253,6 @@ Several options are available to help you monitor your Shared Storage: Once a Shared Storage is [connected](/shared-storage/how-to/connect) to a runtime service, Zerops will create a new folder `/mnt/[shared storage name]` in the runtime service's filesystem. For example, `/mnt/teststorage` for a `teststorage` Shared Storage: - :::note The content of this folder is shared among all containers of the connected runtime service. If you connect multiple runtimes, the content of the folder will be shared among all containers of these services. @@ -25660,7 +24364,6 @@ You can change the auto scaling parameters later. The Static service provides a way to serve static content through a pre-configured Nginx setup. It balances simplicity with the flexibility needed for modern web applications. Deploy an Analog app with static hosting in seconds. All you need is a Zerops account. - ## Quick Start Add a Static service to your project by including this in your `zerops.yaml`: ```yaml title="zerops.yaml" @@ -26264,7 +24967,7 @@ zerops: base: ubuntu@24.04 # OPTIONAL. Build your application buildCommands: - - + - ... ``` Build commands are optional. Zerops triggers each command in the defined order in a dedicated build container, running from the `/build/source` directory. @@ -26425,19 +25128,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a Ubuntu service with hostname = "app" and port = 8080 from another service of the same project, simply use `app:8080`. Read more about [how to access a Ubuntu service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the Ubuntu runtime environment by installing additional dependencies or tools to the runtime base environment. The base Ubuntu environment contains {data.ubuntu.default}, Zerops command line tool and `git` and `wget`. To install additional packages or tools add one or more prepare commands: @@ -26561,25 +25259,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -26601,16 +25293,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Ubuntu application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -26653,25 +25341,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -26694,16 +25376,12 @@ Read more about how the [readiness check works](/ubuntu/how-to/deploy-process#re #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Ubuntu application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -26728,7 +25406,6 @@ Read more about how the [readiness check works](/ubuntu/how-to/deploy-process#re # Ubuntu > How To > Build Process - ## Build process overview Zerops starts a temporary build container and performs the following actions: 1. **Installs the build environment** - Sets up base system and runtime @@ -26745,7 +25422,7 @@ Configure your build process in your `zerops.yaml` file according to the full b The default build environment contains: - {data.ubuntu.default} - [zCLI](/references/cli), Zerops command line tool -- +- ### Customize build environment To install additional packages or tools, add one or more build.prepareCommands to your `zerops.yaml`. :::info @@ -26753,23 +25430,18 @@ The application code is available in the `/build/source` folder in your build co ::: ### Build hardware resources All runtime services use the same hardware resources for build containers: - HW resource Minimum Maximum - CPU cores 1 5 - RAM 8 GB 8 GB - Disk 1 GB 100 GB - Build containers start with minimum resources and scale vertically up to maximum capacity as needed. ### Build time limit The time limit for the whole build pipeline is **1 hour**. After 1 hour, Zerops will terminate the build pipeline and delete the build container. @@ -26916,70 +25588,39 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description - hostname - The unique service identifier. - The hostname of the new database will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Ubuntu service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -26995,7 +25636,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Ubuntu service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -27056,7 +25696,7 @@ It is also a great option when you need a specific version of a technology (like The default runtime environment contains: - {data.ubuntu.default} - [zCLI](/references/cli) -- +- ### When You Need a Custom Runtime Image Since Ubuntu serves as a general-purpose base, you'll likely want to customize it for your specific use case. Common scenarios include: :::important @@ -27190,6 +25830,1014 @@ For advanced configurations or custom requirements: ---------------------------------------- +# Zcp > Concept > How It Works + +ZCP turns product or app intent into a verified deployed app by giving the coding agent two things it normally lacks: current knowledge of the Zerops project and the tools to change, deploy, inspect, and verify it. +## The model in one pass +Product intent enters a project loop: read current state, decide whether the needed services already exist, change the app and its Zerops wiring, deploy, verify from evidence, and return either proof or a concrete blocker. +```mermaid +flowchart TD + intent["Product intent +Build a task board where tasks stay saved after refresh."] + context["ZCP context +live services, env vars, logs, events, +runtime guidance, available operations"] + target["Project fit +use existing services or create missing ones; +select the app runtime, +not the zcp setup service"] + app["Build the app +code, zerops.yaml, env wiring"] + deploy["Deploy through Zerops"] + verify{"Verify real behavior +endpoint or UI"} + fix["Read evidence +logs, events, checks"] + done["Done +URL, endpoint, or UI proof"] + blocker["Blocker +missing credential, decision, +or repeated failure"] + delivery["Future delivery +direct deploy, git push, or CI handoff"] + intent --> context --> target + target --> app --> deploy --> verify + verify -->|fixable| fix --> app + verify -->|verified| done --> delivery + verify -->|needs human| blocker + classDef user fill:#f7fbff,stroke:#2d72d9,stroke-width:1.5px,color:#172033; + classDef zcpbox fill:#eef7f1,stroke:#32845a,stroke-width:1.5px,color:#172033; + classDef work fill:#fffaf0,stroke:#c47f17,stroke-width:1.5px,color:#172033; + classDef done fill:#f5f3ff,stroke:#7157d9,stroke-width:1.5px,color:#172033; + classDef stop fill:#fff1f2,stroke:#d33f49,stroke-width:1.5px,color:#172033; + class intent user; + class context zcpbox; + class target,app,deploy,verify,fix work; + class done,delivery done; + class blocker stop; +``` +The important signals are simple: the agent read current project state, named the target runtime, verified the requested behavior, and explained what remains. +## Where ZCP runs +ZCP can run in two places: +| Path | What runs where | What changes for you | +|---|---|---| +| Remote setup | A `zcp@1` service runs the `zcp` binary inside the Zerops project. **Include Coding Agent** adds the bundled agent CLI and preconfigures it to use ZCP; **Cloud IDE** adds browser VS Code. | The work stays inside the project. The agent can use project-private networking and SSHFS mounts for runtime files. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your editor's agent talks to it. | Your checkout and dev server stay local. Managed services are reached over VPN, and `.env` generation bridges project credentials into your local app. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your editor's agent talks to it. | Your checkout and dev server stay local. Managed services are reached over VPN, and `.env` generation bridges project credentials into your local app. | +The project-scoped control plane is the same idea in both paths. The filesystem, network path, deploy source, and safety profile are different. See [Choose remote or local setup](/zcp/setup/choose-workspace). +## What ZCP reads +ZCP reads live project state instead of relying on a long prompt: +- services and whether they are runtime or managed dependencies, +- runtime layout, such as one app runtime, a dev+stage pair, or a local checkout linked to a Zerops runtime, +- service env-var keys and references, +- build/deploy events, runtime logs, and verification results, +- the current work state when a session is interrupted. +Recovery starts from live state. If a session gets confused or interrupted, ask the agent to read ZCP status before changing anything else. +## What counts as done +A task is not done when code is written, or when a build succeeds. A ZCP task is done when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the URL or a concrete blocker. +Workflow path: [Build with ZCP](/zcp/workflows/build-with-zcp). Exact terms and runtime layouts: [Workflow terms](/zcp/reference/agent-workflow). + +---------------------------------------- + +# Zcp > Glossary + +## Core names +**ZCP** - Zerops Control Plane for coding agents: the project-scoped control plane documented in this section. +**ZCP operations** - the project-scoped operations surface exposed to a coding agent. In MCP clients, this appears as the ZCP MCP server. +**`zcp` binary** - the executable. Runs inside remote setup or on your laptop in local setup. +**zCLI** - the Zerops command-line client for humans, scripts, VPN, and CI. It coexists with ZCP. +**zsc** - the in-container Zerops Setup Control utility used from `zerops.yaml`. +## Where ZCP runs +**Remote setup** - ZCP running inside a Zerops `zcp@1` service in the project. +**`zcp@1` service** - the Zerops service type behind remote setup. +**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI and preconfigures it to use the project's ZCP operations. +**Cloud IDE** - browser-based VS Code served by remote setup. +**AI Agent environment** - a recipe environment preset that creates app services plus remote setup with **Include Coding Agent** enabled. It is a recipe preset, not a separate ZCP setup path. +**Local setup** - the `zcp` binary running on your laptop after `zcp init`, while your editor or CLI agent talks to it. +**Local agent bridge** - local setup with the `zcp` binary connected to a local agent client. +## Workflows and results +**Service setup** - the infrastructure phase behind an app prompt. It uses existing runtime and managed services when they fit, creates missing services when needed, then stops before app code, `zerops.yaml`, or deploy. +**Deploy, verify, and fix** - the app phase. It scopes the runtime, changes code/config, deploys, verifies reachability, verifies requested behavior, and fixes failures. +**Runtime scope** - the app runtime the change targets, such as `appdev`, `appstage`, `app`, or a linked local target. +**Delivery choice** - what happens to future changes after a verified deploy: direct deploy, git-push, or external handoff. +**ZCP status** - a live project read the agent can use after interruption or confusion. Ask for it before letting the agent change anything else. +**Blocker** - a clear stop state with evidence: failure category, runtime in scope, what was tried, and what decision or credential is needed. +## Project and runtime terms +**Runtime service** - a service that runs app code. +**Managed service** - database, cache, queue, search, storage, or similar dependency. It provides connection details; it is not an app deploy target. +**Runtime layout** - which app runtime services ZCP should use: +- `standard` - dev runtime plus explicit stage runtime. +- `dev` - one mutable development runtime. +- `simple` - one runtime with no dev/stage split. +- `local-stage` - local checkout linked to one Zerops runtime as deploy target. +- `local-only` - local checkout with no linked runtime yet. +**Service scaling mode** - Zerops scaling setting such as `HA` or `NON_HA`. Different from runtime layout. +**Stage** - review or promotion target. Stage is not production. +## Credentials +**`ZCP_API_KEY`** - project-scoped Zerops token used by ZCP. +**`GIT_TOKEN`** - git provider credential used by remote git-push delivery when needed. +**`ZEROPS_TOKEN`** - Zerops token commonly used by GitHub Actions or external CI that runs `zcli`. + +---------------------------------------- + +# Zcp > Overview + +ZCP is the Zerops control plane setup that lets a coding agent work from real project state instead of a hand-written operations checklist. It gives the agent current project knowledge, guarded project operations, deploy/verify evidence, and Zerops guidance for connecting an app to its infrastructure. +It can run remotely inside a `zcp@1` service with **Include Coding Agent** enabled, or locally as the `zcp` binary initialized beside your CLI/editor agent. In both cases, the developer stays focused on product intent, technology choices, acceptance criteria, and decisions that require human judgment. +## What ZCP does +ZCP makes four things available to the agent inside a normal Zerops project: +**Project awareness.** The agent can read services, runtime layout, env vars, logs, events, and current work state instead of asking you to describe the project. +**Infrastructure wiring.** The agent can separate the `zcp` setup service, app runtime services, and managed dependencies, then connect the app to the right env vars and service references. +**Deploy and verify loop.** The agent can prove the app works on the selected runtime, not stop after writing code or seeing a green build. +**Delivery choice.** After verified work, the agent can keep deploying directly, push to git, or use external handoff to your CI or a human. +You provide product intent and judgment: what to build, which runtime or stage matters when you care, and what result counts as done. +## A good ZCP session +A ZCP-backed session should be product-led and evidence-based: +- The agent reads the project before changing it. +- It uses existing services when they fit and creates missing infrastructure when they do not. +- It deploys and verifies the requested behavior on the real endpoint or UI. +- It ends with a URL or proof of behavior, or with a blocker that names the missing decision, credential, or repeated failure. +## What you no longer have to do +ZCP is valuable because it removes the operational checklist from the prompt. You should not have to: +- describe every service already in the project, +- decide which env-var references connect the app to managed services, +- copy database credentials, logs, or deploy timelines into chat, +- repeat deploy, verify, log-reading, and URL-reporting instructions on every app task, +- translate a failed build or broken route into a guess before the agent can act, +- choose internal workflow names before describing what you want built. +You still own the product intent, technology constraints, acceptance criteria, credentials that live outside Zerops, and approval for destructive actions. +## Where to start +| Goal | Page | +|---|---| +| Try ZCP once with no local install | [Quickstart](/zcp/quickstart) | +| Understand the model before setup | [How ZCP works](/zcp/concept/how-it-works) | +| Choose remote vs local | [Choose remote or local setup](/zcp/setup/choose-workspace) | +| Add remote setup | [Set up remote ZCP](/zcp/setup/hosted-workspace) | +| Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | +| Build or change an app | [Build with ZCP](/zcp/workflows/build-with-zcp) | +| Decide how finished work ships | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | +| Understand tokens and boundaries | [Trust model](/zcp/security/trust-model) | +| Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | +| Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | +| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | +| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | +## What stays outside ZCP +ZCP is a control plane for agents, not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, scaling, and service references remain canonical for platform behavior. +## Names that matter +- **ZCP** - Zerops Control Plane in its coding-agent setup. +- **Remote setup** - a `zcp@1` service inside the Zerops project. The service runs the `zcp` binary and exposes ZCP to the agent from inside the project. +- **Local setup** - the `zcp` binary installed on your machine and initialized in a project folder with `zcp init`. +- **Include Coding Agent** - the remote setup option that adds the bundled agent CLI and preconfigures it to use ZCP in the service. +- **Cloud IDE** - optional browser-based VS Code in remote setup. +- **`zcp` binary** - the executable. In remote setup it is inside the `zcp@1` service; in local setup you install it and run `zcp init` in a project folder. +For the full vocabulary, including `zcp`, zCLI, zsc, MCP, runtime layouts, and credential names, see the [Glossary](/zcp/glossary). + +---------------------------------------- + +# Zcp > Quickstart + +Remote setup is the fastest first run: no local `zcp` install, no local MCP config, and the bundled agent is already connected to the project. You will open a project with **Include Coding Agent** enabled, give the agent a small app request, and inspect what it verified. +## Prerequisites +- A Zerops account with permission to create a project. +- A login for the bundled agent shown in the dashboard. +## Create a remote setup project +The fastest path is a recipe with the **AI Agent** environment: +1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes). +2. Pick a recipe that matches the stack you want to try. +3. Select the **AI Agent** environment. +4. Deploy the recipe. +The **AI Agent** environment creates the app services plus a `zcp@1` remote setup with **Include Coding Agent** enabled. Keep **Cloud IDE** enabled too so you can open the browser workspace. If you start from an empty project instead, enable **Add Zerops Control Plane (ZCP) service** during project creation and keep **Include Coding Agent** on; the agent can use existing app services or create the missing ones from your prompt. Full setup path: [Set up remote ZCP](/zcp/setup/hosted-workspace). +## Open remote setup +When provisioning finishes, open the `zcp` service in the Zerops dashboard. If **Cloud IDE** is enabled, open its workspace URL; a browser editor opens with the agent connected to this project through ZCP. +Ask for what the app should do. Service setup, deploy, verification, and reporting are part of the ZCP-backed agent contract. +In the agent chat: +```text +Build a task board where tasks stay saved after refresh. +``` +Add constraints only when they change the product, runtime layout, or delivery path: +```text +Build a task board where tasks stay saved after refresh. Use the existing dev+stage pair if this project has one. +``` +```text +After it works, set up git-push delivery to git@github.com:my-org/task-dashboard.git. +``` +## What you should see +A good run should make these points clear: +1. The agent read current project state before changing anything. +2. It named the app runtime it will change. The `zcp` service is the ZCP setup, not the app target. +3. It used existing services when they already fit, or created missing services before app work. +4. It changed code and `zerops.yaml` as needed. +5. It deployed through Zerops and read logs or events if something failed. +6. It verified both platform reachability and the requested dashboard behavior. +7. It ended with a URL or a concrete blocker. +Open the URL the agent gives you. If the page loads but the requested behavior is missing, the task is not done; ask the agent to verify that exact behavior again. +## Next steps +- [How ZCP works](/zcp/concept/how-it-works) - the model behind the run. +- [Build with ZCP](/zcp/workflows/build-with-zcp) - how real app work flows. +- [Choose how finished work ships](/zcp/workflows/delivery-handoff) - direct deploy, git-push, or external handoff. +## Gotchas +- **Deploy success is not done.** The app behavior you requested must be verified on the real URL or endpoint. +- **The `zcp` service is not the app.** Runtime services such as `appdev`, `appstage`, or `app` receive app code and deploys. +- **Production stays out of the agent loop.** Use ZCP in dev/staging projects; promote to production through your CI or release process. + +---------------------------------------- + +# Zcp > Reference > Agent Workflow + +Exact vocabulary for ZCP workflows: runtime layout, verification, delivery, failure state, and completion evidence. Day-to-day app prompts should use outcomes; policies and handoffs can use these names when precision matters. +## Session layers +| Layer | What it is | What changes here | +|---|---|---| +| ZCP setup | Where ZCP runs: remote `zcp@1` service or local `zcp` binary. | No app code should be deployed to the setup itself. | +| Target runtime service | The app runtime: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys. | +| Managed services | Databases, caches, queues, search, storage. | Schema/data operations, never app code deploys. | +| Managed services | Databases, caches, queues, search, storage. | Schema/data operations, never app code deploys. | +The `zcp` service is the control surface, not the app runtime. +## Service setup +Before code work starts, ZCP should identify the app runtime services and managed dependencies. It should use existing services when they fit, create missing services when the project is empty, and resume from live project state if setup was interrupted. +The service setup phase stops before app edits begin; the full ZCP work session then continues into code, deploy, verification, and recovery. +## App work completion contract +For an app intent, ZCP expects the agent to handle code, config, deploy, verify, and retries behind the prompt. A completed session leaves evidence of: +- runtime scope, +- app code and `zerops.yaml` changes, +- a direct deploy for the first verified runtime, +- platform reachability, +- requested behavior on the real endpoint or UI, +- evidence-based retries when something failed, +- a URL or blocker. +## Verification layers +| Layer | What it proves | +|---|---| +| Runtime reachability | Service status, recent error logs, and HTTP probe for eligible runtime services. | +| Requested behavior | The user-requested endpoint, UI state, job result, database state, or stage URL works. | +| Requested behavior | The user-requested endpoint, UI state, job result, database state, or stage URL works. | +Both layers must pass before a session reports completion. +## Runtime layouts +| Layout | Meaning | +|---|---| +| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage must be named when included. | +| `dev` | One mutable development runtime. | +| `simple` | One runtime with no dev/stage split. | +| `local-stage` | Local checkout linked to one Zerops runtime for deploys. | +| `local-only` | Local checkout with no linked deploy target yet. | +| `local-only` | Local checkout with no linked deploy target yet. | +Runtime layout describes which app runtime services ZCP should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. +## Delivery choice +Delivery choice applies after a verified deploy and describes future changes. +| Choice | Meaning | +|---|---| +| Direct deploy | ZCP keeps deploying changes directly to the target runtime. | +| Git push | The agent commits and pushes to a configured remote; any resulting build still needs verification. | +| External handoff | Your CI, release process, or a human owns future delivery. | +| External handoff | Your CI, release process, or a human owns future delivery. | +## Failure categories +| Category | Meaning | Read first | +|---|---|---| +| `build` | Build phase failed. | Build logs and build commands. | +| `start` | Build passed, runtime failed to start or crashed. | Runtime/prepare logs and start command. | +| `verify` | Runtime exists but reachability or behavior failed. | Failing check detail and runtime logs at request time. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and network path. | +| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Structured rejection detail and config files. | +| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | +| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +## Completion evidence +A well-run session can answer: +- which services were used or created, +- which runtime scope was changed, +- which deploy passed, +- which reachability and behavior checks passed, +- which URL or endpoint proves the result, +- which delivery contract applies next, +- or which blocker remains and what evidence supports it. +Operation-name reference lives in [Advanced operations](/zcp/reference/mcp-operations). Recovery shortcuts live in [Troubleshooting](/zcp/reference/troubleshooting). + +---------------------------------------- + +# Zcp > Reference > Index + +Exact terms, operation names, and recovery detail live here. Day-to-day app work starts in [Build with ZCP](/zcp/workflows/build-with-zcp). +| Need | Page | +|---|---| +| Name the workflow phases, runtime layouts, verification layers, and delivery choices | [Workflow terms](/zcp/reference/agent-workflow) | +| Look up common MCP operation names for agent-client policy or debugging | [Advanced operations](/zcp/reference/mcp-operations) | +| Turn a running service into a re-importable bundle | [Package a running service](/zcp/workflows/package-running-service) | +| Recover a stuck or confused session | [Troubleshooting](/zcp/reference/troubleshooting) | +| Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | +| Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | + +---------------------------------------- + +# Zcp > Reference > Mcp Operations + +Most users never need operation names. They matter when you configure agent-client policy, debug an MCP integration, or automate ZCP directly. In normal app work, ask the agent for outcomes. Recipe-authoring and ZCP-internal maintenance operations are intentionally out of scope. +## Permission classes +| Class | Meaning | +|---|---| +| Read-only | Inspect state, logs, events, guidance, or verification output. | +| Mutating | Change services, env vars, scaling, deploy state, or lifecycle. | +| Operational setup | Coordinate workflows, mounts, imports, recipes, delivery setup, or generated local files. | +| Operational setup | Coordinate workflows, mounts, imports, recipes, delivery setup, or generated local files. | +## Read-only operations +| Operation | Purpose | +|---|---| +| `zerops_discover` | Read services, ports, env-var keys, and current state. | +| `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | +| `zerops_events` | Read service-scoped project activity, deploys, builds, scaling, and failures. | +| `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | +| `zerops_knowledge` | Fetch ZCP guidance or platform knowledge for the current state. | +| `zerops_process` | Check a known async process; cancel is a mutating action. | +| `zerops_process` | Check a known async process; cancel is a mutating action. | +## Mutating operations +| Operation | Purpose | +|---|---| +| `zerops_deploy` | Ship code through the Zerops build and deploy pipeline. | +| `zerops_env` | Read, set, delete, or generate env vars and local `.env` files. | +| `zerops_manage` | Start, stop, restart, reload, or connect storage. | +| `zerops_scale` | Change CPU, RAM, disk, CPU mode, or container autoscaling where supported. `HA`/`NON_HA` is set at service creation. | +| `zerops_subdomain` | Enable or disable public subdomain access. | +| `zerops_delete` | Delete a service. Explicit named approval is required. | +| `zerops_delete` | Delete a service. Explicit named approval is required. | +## Operational setup +| Operation | Purpose | +|---|---| +| `zerops_workflow` | Track, recover, or configure ZCP work sessions; also carries the package-running-service export flow. | +| `zerops_import` | Import project/service definitions. Destructive override is gated. | +| `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | +| `zerops_preprocess` | Expand Zerops preprocessor expressions. | +| `zerops_export` | Read platform project/service export YAML and service metadata. | +| `zerops_export` | Read platform project/service export YAML and service metadata. | +## Remote and local differences +| Area | Remote setup | Local setup | +|---|---|---| +| Files | Runtime files through SSHFS/project containers | Local working directory | +| Deploy source | Project service or batch deploy inside Zerops | Local `workingDir` | +| Dev server | ZCP can run/inspect project-container dev processes | Your local tool owns the dev server | +| Env bridge | Project env is available in the project | `.env` generation resolves project references for local app use | +| Git | Workspace-managed credentials | User's local git credentials | +| Git | Workspace-managed credentials | User's local git credentials | +## Confirmation gates +Two operations require explicit care: +- **Service deletion** requires explicit user approval in the current conversation, by service name. +- **Destructive import override** first refuses and names what would be replaced; a second call must acknowledge the same targets. +See [Tokens and credentials](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions) for the user-facing confirmation flow. + +---------------------------------------- + +# Zcp > Reference > Troubleshooting + +Start recovery from current project state, not from chat memory. +```text +Read ZCP status and tell me where this project stands before changing anything. +``` +That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. +## Failure categories +| Category | Meaning | First move | +|---|---|---| +| `build` | Build failed before a runtime could start. | Read build logs and `zerops.yaml` build steps. | +| `start` | Build passed, runtime failed to start or crashed. | Read runtime/prepare logs and check start command, ports, and env vars. | +| `verify` | Runtime exists, but reachability or requested behavior failed. | Read the failing check, HTTP response, and runtime logs at request time. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Check the named network path and, for local setup, VPN. | +| `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Read the structured rejection and fix the named field. | +| `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | Fix the named credential; do not rotate unrelated tokens. | +| `other` | No known category matched. | Read raw events/logs and stop if the same unknown failure repeats. | +| `other` | No known category matched. | Read raw events/logs and stop if the same unknown failure repeats. | +## Common symptoms +| Symptom | Likely move | +|---|---| +| Build log shows missing command/module/lock file | Fix build commands, manifests, deploy files, or dependencies. | +| Runtime is `READY_TO_DEPLOY` or restarts continuously | Read runtime logs; the start command or env contract is usually wrong. | +| Public URL returns 502 or connection error | Check port binding, `0.0.0.0`, subdomain readiness, and whether the service is HTTP-eligible. | +| Deploy succeeded but feature is broken | Treat it as behavior verification failure, not deploy success. | +| Local app cannot reach `db`, `redis`, or storage | Bring VPN up and regenerate `.env` if project env changed. | +| Agent targets `zcp` as the app | Correct it to the runtime service. | +| Agent loops on the same failure | Ask for category, evidence read, fixes attempted, and the next human decision. | +| Agent loops on the same failure | Ask for category, evidence read, fixes attempted, and the next human decision. | +Public subdomain access is enabled on first deploy for eligible HTTP runtimes. Workers and non-HTTP services do not get a useful public URL by design. +## When to stop +Stop the loop when the agent cannot make progress with new evidence, needs a missing credential, is about to delete or replace a service, or the target runtime/stage is ambiguous. +Before destructive recovery, read service-scoped events, logs, deploy/verify result, and git history when delivery uses git-push. +## Related +- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) +- [Workflow terms](/zcp/reference/agent-workflow) +- [Tokens and credentials](/zcp/security/tokens-and-project-access) +- [Set up local ZCP](/zcp/setup/local-agent-bridge) + +---------------------------------------- + +# Zcp > Security > Production Policy + +Rule: keep ZCP in a development or staging Zerops project. Production should be a separate project without a `zcp` service; promotion happens through CI or a release pipeline with production-scoped credentials. +The platform doesn't stop you from adding a `zcp` service to a production project. Treat the dev/staging boundary as load-bearing because crossing it puts an agent inside production, not because Zerops will refuse the configuration. +## Why a separate production project +The clean separation is the project boundary: ZCP runs where the agent works, while production runs in its own 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 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 with no `zcp` service, 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 architecture pattern production uses — same runtime version, same managed service types, same build/deploy path — before promotion to the separate production project. +Use stage as the production-like rehearsal: +- The agent develops on dev (`appdev` in the `standard` runtime layout). +- 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-like 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 operational access to 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-like 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 ZCP setup | +| Run the agent's deploys directly into production from a development session | Production deploys come from the production project's pipeline, not the development ZCP setup | +## 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. +- [Troubleshooting](/zcp/reference/troubleshooting) — recover or take over before promoting. +- [Backup](/features/backup) — the production data safety net that operates outside ZCP. +- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) — the dev/stage loop this policy assumes. + +---------------------------------------- + +# Zcp > Security > Tokens And Project Access + +ZCP enforces the project boundary at three layers: token shape (one project, exactly), where the token lives (platform-injected for remote setup, in `.mcp.json` for local setup), and confirmation gates on operations that are not reversible from inside the conversation. +## Recommended token shape — one project, full access +ZCP requires a Zerops API token that resolves to **exactly one project** at startup. For normal agent workflows, use **Custom access per project** scoped to a single project with **Full access**. Read-only tokens can pass startup, but they fail as soon as the agent tries to deploy, write env vars, or operate services. +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 wrong shapes before the agent can operate the project. +| Token shape | What ZCP does | Why | +|---|---|---| +| Multi-project, including account-wide full access | Refuses to start | One ZCP process equals one project; ZCP won't pick which one. | +| No project access | Refuses to start | 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 to start | The token no longer authenticates against the Zerops API. | +| Expired or revoked | Refuses to start | The token no longer authenticates against the Zerops API. | +The fix is always the same: generate a per-project token and replace `ZCP_API_KEY` on the surface ZCP reads from. In remote setup, that value is injected into the `zcp` service environment. In local setup, it is the `env` block in `.mcp.json`. +## Where the token lives - remote vs local {#where-the-token-lives--remote-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: +| Setup | Where `ZCP_API_KEY` comes from | Who provisions it | +|---|---|---| +| Remote setup | The `zcp` service container env | Zerops platform - injected automatically when the service boots | +| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You - added manually after `zcp init` | +| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You - added manually after `zcp init` | +In remote setup, the service starts with `ZCP_API_KEY` populated. If **Include Coding Agent** is enabled, the bundled agent uses that environment automatically. +In local setup, `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 itself, against the Zerops API | Container env (remote) or `.mcp.json` env block (local) | +| `GIT_TOKEN` | A git push from remote setup to a remote (GitHub, GitLab) | Remote: secret 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 | +| `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 remote setup is the one pushing. In local setup, 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 `zcp` service 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** — an 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. Delivery setup should place it in the CI secret store for the repository that runs the deploy. +## Rotation +Rotate in the Zerops dashboard, then propagate to the consuming surface: +- **Remote setup** — the service env value updates; ZCP picks up the new value the next time the `zcp` service boots or restarts. +- **Local setup** — paste the new token into the `env` block of `.mcp.json`, then restart your agent client 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. | Requires explicit approval in the same conversation, including the service name. Remote setup also blocks deleting the `zcp` service it is running in. | +| **Wholesale service replacement** from an import on a service with prior failed deploy history | First returns a summary of what would be replaced and refuses to continue. The agent must show that evidence before asking you to confirm. | +| **Wholesale service replacement** from an import on a service with prior failed deploy history | First returns a summary of what would be replaced and refuses to continue. The agent must show that evidence before asking you to confirm. | +ZCP itself adds confirmation only for the two operations above. Your agent client or team policy may still prompt before tool calls. Deploys, env changes, lifecycle actions, restarts, scaling, and public-access changes do not get an additional ZCP-specific confirmation gate. Most are reversible by another call. A few are operationally destructive even though they are not gated by ZCP (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. +- **Replace-as-reset** — replacing a service from an import 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. +Threat model and hard refusals: [Trust model](/zcp/security/trust-model). +## Gotchas +- **Multi-project tokens are refused at startup, not at first deploy.** An account-wide 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 in remote setup; `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), and review service-scoped events, logs, deploy results, and git history before approving destructive recovery. +## 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. +- [Set up local ZCP](/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`. + +---------------------------------------- + +# Zcp > Security > Trust Model + +The security model starts with one rule: a ZCP process operates one Zerops project. Remote and local setup share that project boundary, but they expose different surroundings to the agent. +## Remote setup or local setup +**Remote setup** is the project-contained path. +- ZCP runs in a `zcp@1` service inside the Zerops project. +- When **Include Coding Agent** is enabled, the bundled agent CLI runs there and is preconfigured to use ZCP. +- It uses a project-scoped token and reaches the project's private network. +- Runtime files are visible only when they are mounted into remote setup. +- Safety profile: **project-contained by design** - no direct access to your laptop, home directory, or other projects. +**Local setup** is the supervise-the-client path. +- The agent runs on your laptop. +- It can touch the same project surface, **plus whatever the agent client can reach on your laptop**. +- It reaches project services over VPN, plus everything else your laptop can already reach. +- Safety profile: **supervise the agent client** - ZCP stays project-scoped, but the agent process inherits your user. +Either path is the right choice in context. Local setup 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 exposing operations. A token with access to no projects is refused; a token with access to multiple projects is refused. The intended token form is 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). | +| 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. +## Remote setup specifics +Remote setup is the project-contained path. A few specifics: +- **`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. +- **Project access stays inside the project.** The `zcp@1` service can read project env and reach project services so the agent can build against real dependencies without you copying credentials around. +- **Filesystem reach is narrower than network reach.** The agent reaches project services over the private network, but only sees runtime files when they are mounted into remote setup. +- **A terminal in remote setup has the same project-scoped access as the agent.** Convenient, and the reason production stays in a separate project. +## Local setup specifics +Local setup 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; the binary doesn't merge local state across them. +- **VPN setup is outside ZCP's authority.** Bringing up the Zerops VPN requires admin or root approval on your laptop. ZCP can tell 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. +- **Local setup 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: [Set up local ZCP](/zcp/setup/local-agent-bridge). +## Audit surface +Zerops records platform-side evidence: service events, deploy/build results, runtime logs, lifecycle actions, scaling, and public-access changes. It does not record the agent's private reasoning, every shell edit, browser-helper actions, or prompt history outside your agent client. +When taking over from an agent, read evidence in this order: service-scoped events, runtime logs, deploy/verify result, then git history when delivery uses git-push. +## What ZCP refuses by design +ZCP refuses boundaries that should never be inferred: tokens that do not resolve to exactly one project, remote self-deletion, and destructive replacement before the agent has read the relevant failure evidence. Destructive confirmation details live in [Tokens and credentials](/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. +- [Troubleshooting](/zcp/reference/troubleshooting) — recover when the agent or session state drifts. +- [Production boundary](/zcp/security/production-policy) — keep production outside the agent loop. + +---------------------------------------- + +# Zcp > Setup > Choose Workspace + +Choose where the ZCP-backed agent runs. ZCP gives the agent the same project-scoped Zerops toolset in both paths, but the filesystem, network path, deploy source, and safety profile are different. +Use remote setup for the first run and project-contained isolation. Use local setup when your checkout, editor, dev server, and git credentials should stay on your machine. +## Short rule +**Remote setup** is the default first run: no local install, project-contained isolation, optional bundled agent, optional browser IDE. +**Local setup** fits when you want to keep your editor, shell, checkout, local dev server, and git credentials. +## Compare the paths +**Remote setup** +- ZCP runs inside the Zerops project in a `zcp@1` service. +- The agent runs inside that service when **Include Coding Agent** is enabled. +- Runtime files can be mounted through SSHFS. +- Managed services are reached over the project-private network. +- Deploy source, env vars, and git credentials live inside the project service. +- Safety profile: project-contained by design. +**Local setup** +- ZCP runs on your laptop as the `zcp` binary. +- The agent runs in your local editor or CLI after `zcp init`. +- App work happens in your local working directory. +- Managed services are reached from your app and shell through Zerops VPN. +- Deploy source and git credentials are your local working directory and local git setup. +- Safety profile: ZCP is project-scoped, but the agent client runs as your user. +## What changes in practice +In remote setup, a `zcp@1` service runs the `zcp` binary inside the Zerops project. Enable **Include Coding Agent** when you want the service to add the bundled agent CLI and preconfigure it to use ZCP. Enable **Cloud IDE** when you also want browser-based VS Code. Runtime files can be mounted into the service, and managed services are reachable over the project-private network. +In local setup, the agent works in your local checkout. You install the `zcp` binary and run `zcp init` in the project folder so the local agent can talk to ZCP. ZCP talks to the Zerops API directly, while your local app and shell need `zcli vpn up` to reach managed services by hostname. +After setup, ZCP reads whether the project has one runtime, a dev+stage pair, or a local checkout linked to a runtime. You usually state the outcome, not the label: dev+stage, one dev runtime, or local deploys to a named runtime. Exact labels live in [Workflow terms](/zcp/reference/agent-workflow#runtime-layouts). +Stage is a review or promotion target, not production. Production should live in a separate Zerops project; see [Production boundary](/zcp/security/production-policy). +## Next steps +- [Set up remote ZCP](/zcp/setup/hosted-workspace) +- [Set up local ZCP](/zcp/setup/local-agent-bridge) +- [Build with ZCP](/zcp/workflows/build-with-zcp) + +---------------------------------------- + +# Zcp > Setup > Hosted Workspace + +Remote setup is a `zcp@1` service inside your Zerops project. It keeps ZCP, project-scoped credentials, private-network access, and optional agent tooling inside the project boundary. +Enable **Include Coding Agent** when you want Zerops to add the bundled agent CLI and preconfigure it to use ZCP in that service. Enable **Cloud IDE** when you also want browser-based VS Code for watching or intervening. +After provisioning, the service is ready for your first app instruction. If **Cloud IDE** is enabled, you can open the browser workspace from the dashboard; otherwise use the access method configured for the service. +Remote and local setup have the same ZCP project boundary but different work surfaces. The comparison lives in [Choose remote or local setup](/zcp/setup/choose-workspace); the broader human development modes live in [Local & Remote Development](/features/local-remote-development). +## Two paths to remote setup +Zerops provisions the service for you - no local install, no MCP config to write by hand, 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 a `zcp@1` service with **Include Coding Agent** enabled. +- **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 base result: a `zcp@1` service inside the project with `ZCP_API_KEY` injected automatically and scoped to this project. **Include Coding Agent** adds the preconfigured agent. **Cloud IDE** adds the browser editor. +## Path A — recipe + AI Agent environment +Recipes are the quickest path when their stack matches the runtime layout 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@1` service with **Include Coding Agent** enabled 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 with a bundled agent is added. 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. Keep **Include Coding Agent** enabled to add and preconfigure the bundled agent inside the service. Use **Configure** when you need to choose authentication or adjust Cloud IDE access; otherwise leave the defaults 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 service +Open the `zcp` service from the Zerops dashboard after it reaches running state. +1. Open the project in the [Zerops dashboard](https://app.zerops.io/). +2. Open the `zcp` service. +3. If **Cloud IDE** is enabled, open the service workspace URL. Code-server launches in a new tab. If the service is exposed on a `.zerops.app` subdomain, see [Public access](/features/access) for the routing model. +If **Cloud IDE** is disabled, remote setup can still exist as a project-contained service, but there is no browser editor to open. Use the access method configured for the service, such as SSH, or enable Cloud IDE when you want a browser workspace. +## Run the agent +When **Include Coding Agent** is enabled, the bundled agent starts already connected to this project through ZCP. In the Cloud IDE path, the terminal has the bundled agent available with `ZCP_API_KEY` in its environment. +For a connection sanity check, ask the bundled agent: +```text +List the services in this project and tell me which are runtimes versus managed dependencies. +``` +A working setup answers with the runtime services (for example `appdev`, `appstage`) and managed services (for example `db`), and says whether the project has one runtime or a dev+stage pair. +For real work, ask for the app behavior, not the Zerops procedure: +```text +Build a task board where tasks stay saved after refresh. +``` +Discovery, service setup, deploy, verification, and final reporting happen behind that app request. +If **Include Coding Agent** is disabled, the `zcp@1` service exists but no preconfigured agent is waiting inside it. Enable the option or use [local ZCP](/zcp/setup/local-agent-bridge) from your own agent client. +After the first setup pass, runtime services can be SSHFS-mounted into the service filesystem with one folder per runtime. 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 remote setup. +If you added ZCP to an existing project, the agent should inspect existing services and use them instead of creating duplicates. That is handled as part of the first product prompt; the details are explained in [Service setup behind the prompt](/zcp/workflows/create-or-adopt-services). +When Cloud IDE is enabled, the browser workspace is persistent. Close the browser tab and reopen the workspace link later; editor state, open files, and the agent session survive across reconnects as long as the `zcp` service is running. +## If connection fails +If the agent can't reach ZCP, the service may still be starting. Wait for it to reach running state in the dashboard and reload the workspace. If **Include Coding Agent** was not enabled, add it or use local ZCP from your own agent client. +At this point, the agent can receive the first app instruction. The [Quickstart](/zcp/quickstart) shows the shortest end-to-end run. +## Customize remote setup +Remote setup is a normal Zerops service, so teams can customize it like one: add tools, preload team config, or run helper processes next to the agent and Cloud IDE. Keep this separate from app runtime work; deploy app code to runtime services, not to `zcp`. +## Gotchas +- **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. If the agent targets `zcp` as the app runtime, stop and correct the target. +- **Don't set `ZCP_API_KEY` by hand in remote setup.** It's platform-injected. Manual override risks breaking the agent's access. (Local setup 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 the bundled agent. Until the service reaches running state, the workspace URL may return an empty page or error. + +---------------------------------------- + +# Zcp > Setup > Local Agent Bridge + +Local setup runs the `zcp` binary on your laptop, initialized in one project directory. The agent edits your checkout, your tools run the dev server, and ZCP deploys from your working directory to the linked Zerops runtime. +`zcp init` writes the local MCP configuration for that folder, so a compatible local agent client can talk to ZCP as the `zerops` MCP server. With `zcli vpn up `, your laptop also joins the project's private network - the same network your services use. +Local setup is newer than remote setup. Expect install details and generated project files to move between releases. +Use this path when you already have a comfortable local setup - your editor, your shell, your dev server - and want a coding agent that can drive Zerops from the same machine. If you're evaluating ZCP for the first time, [remote setup](/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. ZCP is what you add when you also want a coding agent operating the project from the same machine: discovering services, generating local env snapshots, deploying, reading logs, verifying. +Security note: ZCP itself stays project-scoped, but the agent client runs as your local user. Set your client permissions accordingly. +## 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 compatible local agent client installed and logged in. ZCP doesn't see the agent client's subscription or login credentials. +## Get a project-scoped Zerops token +ZCP needs a Zerops API token scoped to **one project**. Multi-project tokens, including account-wide full-access tokens, are refused at startup. Generate one in the Zerops dashboard, then keep the value handy for the local MCP configuration step. Full walkthrough, rejection rules, and rotation: [Tokens and credentials](/zcp/security/tokens-and-project-access). +## Install `zcp` +```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. +## Run `zcp init` in your project folder +In a terminal in your project's working directory: +```bash +zcp init +``` +`zcp init` writes the local MCP configuration and agent instructions for this project. Re-running it may refresh generated config; after rerunning it, confirm `.mcp.json` still contains `ZCP_API_KEY` before launching the agent. +## Configure `zcp` as the local MCP server +`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 your agent client from the 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--remote-vs-local). +The agent client starts and lists the MCP servers it found. The `zerops` server should be in the list; that is the local ZCP connection. +### 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 the agent client from the wrong directory. +## Connect private services with `zcli vpn up` +Your laptop reaches managed services (databases, caches, storage) through the Zerops project [VPN](/references/networking/vpn). ZCP 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. +After the VPN is up, project services resolve by hostname (`db`, `cache`, etc.) from your laptop. +## Local env bridge +When a local app needs project credentials, ask for the product change and let ZCP generate the local env bridge as part of that work. For a standalone sanity check, you can ask: +```text +Use ZCP to generate a .env file for my local app. +``` +ZCP resolves the project env references your app would see when deployed and writes a `.env` snapshot in your working directory. Your local app uses the same hostnames and credentials, reached over VPN. Regenerate after changing project env variables. +## Linked deploy target +Local setup needs one Zerops runtime linked as the deploy target, often a stage runtime, 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. Ask the agent to link a runtime by hostname when ready. +With a linked runtime, ZCP can deploy from your working directory. Without one, it can still inspect the project and generate env snapshots, but deploys need a target runtime first. +## 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. Remote ZCP can give the agent SSHFS access; local ZCP 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; the agent client 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 the agent client both expect it. +- **The `.env` is a snapshot.** Regenerate after changing project env variables, not before. +- **Production stays out of this loop.** Local ZCP is for development and staging projects. +## Next steps +- [Deploy, verify, and fix](/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 remote or local setup](/zcp/setup/choose-workspace) - remote vs local and runtime layouts. + +---------------------------------------- + +# Zcp > Workflows > Build And Verify App + +After the target runtime and dependencies are known, the agent can change code and `zerops.yaml`, deploy the runtime, fix failures from evidence, and verify the result. +A ZCP task is done only when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the working URL or a concrete blocker. +## What the agent does +ZCP treats deploy as diagnostics and verification as the completion gate. The user does not have to script this sequence; it is the expected path behind an app prompt. +- **Runtime scope.** The agent identifies which runtime changed. +- **Code and `zerops.yaml`.** The agent changes app files and runtime config where needed. +- **First deploy.** The first verified runtime deploy goes through ZCP. +- **Runtime reachability.** The agent checks that the runtime answers on the real platform path. +- **Requested behavior.** The agent proves the endpoint, URL, UI state, row, job result, or other acceptance evidence. +- **Evidence-based fix.** If either verification layer fails, the agent reads new logs/events/check output, fixes the cause, and redeploys. +A green build or running runtime is not done until the requested endpoint, UI, or state is checked on the real URL. Retries should use new evidence, not repeat the same deploy. +## Runtime scope is a precondition +Before editing, the agent should make the runtime service visible: `appdev`, `appstage`, `app`, or the linked runtime in a local setup. Managed services such as databases and caches are dependencies, not deploy targets. +If the agent starts writing files before it knows the runtime scope, treat that as a correction signal: it should read ZCP status and identify the target before continuing. +## Deploy +The first deploy uses the direct ZCP deploy path. Delivery choices such as git-push or external handoff apply after a verified deploy exists. +Deploy failures point to different evidence: +| Failure point | Read first | +|---|---| +| Build failed | Build logs and `zerops.yaml` build steps | +| Runtime started then crashed | Runtime logs and start command | +| App is up but route fails | Runtime logs at request time and behavior check output | +| Local app cannot reach managed services | VPN state and generated `.env` values | +| Local app cannot reach managed services | VPN state and generated `.env` values | +## Verify +Verification has two layers: +| Layer | What it proves | +|---|---| +| Platform reachability | The service is running, recent error logs are checked, and HTTP runtime services answer a probe when eligible. | +| Requested behavior | The thing you asked for works: endpoint body, UI state, database row, job result, or stage URL. | +| Requested behavior | The thing you asked for works: endpoint body, UI state, database row, job result, or stage URL. | +Both layers must pass before the agent reports completion. +## Fix or stop +The agent should name the failure point in plain language, read fresh logs/events/check output, fix the indicated cause, and redeploy. A repeated retry with no new evidence is not progress. +Stop and ask for a decision when scope is ambiguous, credentials are missing, destructive recovery is needed, or several evidence-based retries hit the same failure. +## Review and take over +The platform records deploys, events, runtime logs, and lifecycle actions. It does not record every agent thought, browser click, or shell edit. When taking over, ask for ZCP status first, then read service-scoped events, logs, deploy/verify results, and git history if delivery uses git-push. +## Gotchas +1. **Deploy success is not verify success.** A green build with a broken feature is not complete. +2. **Stage is explicit.** In dev+stage projects, the agent should say when stage is included before changing or verifying it. +3. **Secrets belong in env vars.** If the app needs a third-party API key, the agent should ask for an env-var path, not put secrets in source. + +---------------------------------------- + +# Zcp > Workflows > Build With Zcp + +Start with what the app should do, not with tool calls or workflow names. +```text +Build a task board where tasks stay saved after refresh. +``` +Behind that prompt, the agent should inspect the project, choose a target runtime, make the app change, and use real deploy evidence until the requested behavior is proven. ZCP supplies the project knowledge and tool access needed for that loop: live services and env state, Zerops guidance, deploys, logs, verification, and guarded infrastructure changes. +Do not make the prompt longer to restate completion expectations such as deploy, verification, or returning the URL. The words you add should change the app, the stack, the runtime layout, the acceptance criteria, or the delivery path. +You also do not need to paste an infrastructure inventory, env wiring plan, or log summary into the prompt. ZCP makes those available to the agent from the live project. +## What happens after your prompt +The agent should make this path visible as it works: +**Project discovery.** Read what exists now: runtime services, managed services, env vars, logs, recent events, and current work state. +**Service setup.** Use existing services when they fit, or create the missing runtime and managed dependencies before app code starts. +**Runtime scope.** Identify the app runtime that will receive code, such as `appdev`, `appstage`, `app`, or a linked local deploy target. The `zcp` service is never the app target. +**Code, deploy, verify, fix.** Change app files and `zerops.yaml`, deploy through Zerops, verify reachability and requested behavior, read evidence on failure, and retry from the cause. +**Delivery after proof.** After a verified deploy exists, keep direct deploy, push to git, or use external handoff to CI or a human. +Advanced export/reuse path: [Package a running service](/zcp/workflows/package-running-service). +## What the final answer should contain +For a completed app task, the agent should report: +- which runtime service changed, +- which deploy passed, +- which URL, endpoint, or UI state proved the requested behavior, +- any env vars, managed services, or delivery settings touched, +- the delivery choice if one was set. +If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the next decision needed from you. + +---------------------------------------- + +# Zcp > Workflows > Create Or Adopt Services + +When an app prompt needs infrastructure, ZCP should discover what already exists before creating runtimes or managed services. This usually happens because you asked for a product outcome, not because you explicitly asked for infrastructure. +For example, `Build a task board where tasks stay saved after refresh.` may require an app runtime and database. `Use the existing Laravel services` tells the agent to reuse what is already there instead of creating duplicates. +Behind that prompt, the agent should answer: +- Which runtime service will hold app code? +- Which managed services does the app depend on? +- Do those services already exist, or should the agent create them? +- Are they running and visible to ZCP? +This part does **not** write application code, create `zerops.yaml`, run the first deploy, or verify behavior. Those belong to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). +## Starting situations +| Situation | What should happen | +|---|---| +| Runtime services already exist | Use them. Identify runtimes and managed dependencies. Do not recreate or rename services just to begin. | +| Project has only the `zcp` service | Treat it as empty from the app point of view. Create or import the requested service layout. | +| Project is empty and request matches a known stack | A recipe may be a good starting base, but the starter still has to be changed into the requested app. | +| Project is empty and request needs a custom layout | Create a custom service plan before app work starts. | +| Setup was interrupted | Resume from current ZCP status instead of starting over. | +| Setup was interrupted | Resume from current ZCP status instead of starting over. | +The `zcp` service is the ZCP setup, not the application runtime target. +## Prompt examples +Product prompts can imply the service setup; they do not need to spell it out: +```text +Build a Node.js API with PostgreSQL. Use the existing dev+stage pair if this project has one. +``` +```text +Use the existing Laravel services and build a small notes app. +``` +```text +Add saved tasks to this task board. +``` +The agent should explain whether it used existing services or created new ones, then move into app work when infrastructure is known. +## Done state +Service setup is done when the app runtime and managed dependencies are known, any new services are visible, and the agent is ready to start app code, `zerops.yaml`, deploy, and verification work. +When files, framework setup, deploys, logs, or behavior checks appear, the work has moved to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). +## Failure signals +| Symptom | What to do | +|---|---| +| Agent targets `zcp` as the app | Correct the target to the runtime service. | +| Agent recreates existing runtimes | Ask it to inspect current services and use what exists. | +| Agent writes app files during service setup | Stop and finish infrastructure first, or move explicitly to app work. | +| Service creation fails | Treat it as a hard stop until the failure is understood. | +| Service creation fails | Treat it as a hard stop until the failure is understood. | + +---------------------------------------- + +# Zcp > Workflows > Delivery Handoff + +Delivery is decided after the app works. You can include delivery intent in the original product prompt, but ZCP applies it only after a verified deploy exists. The first deploy uses ZCP's direct deploy path; the delivery choice records how future changes should be delivered. +## Three delivery choices +**Keep direct deploy** for fast iteration, solo projects, and demos. The agent keeps deploying through ZCP directly. +**Push to git** when you want commits, reviews, or a repository-driven build. The agent commits and pushes to a configured remote; a build integration may run afterward. +**External handoff** when your CI, release process, or a human owns delivery. ZCP records what happened but does not initiate future deploys. +You can ask in plain language: +```text +After the app verifies, set up git-push delivery to git@github.com:my-org/task-dashboard.git. +``` +```text +Keep ZCP deploying directly for now. +``` +```text +My CI takes over after this verified deploy. +``` +## Push to git +Git-push needs committed code, a remote URL, and credentials that can push. +Remote and local setup differ: +- Remote setup uses credentials configured inside the `zcp` service, commonly a fine-grained git token. +- Local setup uses your local git credentials and working directory. +A push is not proof that the app works. If a webhook or GitHub Actions build runs from the push, the agent should observe the build result and verify the app afterward. +## Change later +Delivery choice is not permanent. You can move from direct deploy to git-push later, add a build integration later, or switch to external handoff for a release that needs human control. +## Gotchas +1. **Delivery choice is not a hidden deploy button.** It records the future path after the app already works. +2. **External handoff is not a deploy by itself.** It means a human or external system owns delivery. +3. **GitHub Actions uses a Zerops token.** The Actions secret for `zcli` deploys is `ZEROPS_TOKEN`, not your GitHub token. + +---------------------------------------- + +# Zcp > Workflows > Package Running Service + +Use packaging when a deployed runtime should become a reusable import bundle. This is an advanced handoff or reuse workflow, not the normal way to deploy the next change. 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 layout 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 the next app change deployed. For that, ask for the app result; direct deploy is the expected ZCP path behind the request. +- You want to deploy the next change to an existing production project. Keep production in its own Zerops project and operate it through Zerops UI, the API, or CI. +Packaging does not keep a long-running session. If interrupted, the agent reads live state and re-runs the export flow; you only decide when the dev or stage runtime, or env classification, is ambiguous. +## 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 is chosen, 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. | +| `plain-config` | Literal runtime config (`LOG_LEVEL`, `NODE_ENV`, feature flags) | The literal value, verbatim. | +ZCP does not guess these buckets from key names alone. The agent should classify them from source code: if the value is read by a Stripe SDK call it is `external-secret`; if it appears as `${db_*}` from a managed service it is `infrastructure`. The packaging response carries env *keys* and redacts values; the agent fetches values only 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 +#zeropsPreprocessor=on +# zerops-project-import.yaml — paste into a fresh Zerops project +project: + name: demo + envVariables: + APP_KEY: )> + 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 [Zerops preprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `` directives (here `)>` for `APP_KEY`), `#zeropsPreprocessor=on` must be 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). +Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen handoff path expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through the normal [handoff path](/zcp/workflows/delivery-handoff). +## 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 | +| Production config | Keep production in a separate Zerops project | +## Gotchas +1. **Packaging is not the delivery path by itself.** It produces the bundle. Commit and push happen through your chosen [handoff path](/zcp/workflows/delivery-handoff). +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. +- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) — the verified runtime this packages. +- [How ZCP works](/zcp/concept/how-it-works) — why packaging stays stateless. + +---------------------------------------- + # Zerops Yaml > Base List This is a list of all currently supported versions of technologies that can be used for build.base and run.base sections in `zerops.yaml`. @@ -27197,90 +26845,63 @@ This is a list of all currently supported versions of technologies that can be u Versions listed on the same line are aliases of the same underlying version. ::: ## Runtime services - Service Type Supported OS Versions - Build / Runtime - Bun `ubuntu` / `alpine` - Deno `ubuntu` - .NET `ubuntu` / `alpine` - Elixir `ubuntu` / `alpine` - Gleam `ubuntu` - Go `ubuntu` / `alpine` - Java `ubuntu` / `alpine` - Node.js `ubuntu` / `alpine` - Python `ubuntu` / `alpine` - Rust `ubuntu` / `alpine` -     Build Runtime - PHP + Apache `ubuntu` / `alpine` - PHP + nginx `ubuntu` / `alpine` - ## Static services - Service Type Supported OS Versions - Build Runtime - nginx `ubuntu`/`alpine` - - static `ubuntu`/`alpine` - - ## Containers and virtual machines - Service Type Supported OS Versions - Build Runtime - + Docker + `alpine` + - Alpine `alpine` - Ubuntu `ubuntu` - - Docker - `ubuntu` - - - ---------------------------------------- @@ -27314,7 +26935,7 @@ Specifies the directory where the command will be executed. If not set, it defau ## Example Configurations Here’s a basic example of how to set up a cron job in your service's `zerops.yaml`: ```yaml -run: +run: crontab: - command: "date >> /var/log/cron.log" timing: "0 * * * *" @@ -27323,7 +26944,7 @@ This configuration logs the current date to `/var/log/cron.log` every hour. ### Running on Multiple Containers By default, cron jobs run on a single container, even if multiple containers exist for the service. To execute a command across all containers, you can use the `allContainers` parameter: ```yaml -run: +run: crontab: - command: "rm -rf /tmp/*" timing: "0 0 * * *" @@ -27333,7 +26954,7 @@ This example removes temporary files from all containers every day at midnight. ### Custom Working Directory You can also specify a custom working directory for your commands using the `workingDir` parameter: ```yaml -run: +run: crontab: - command: "php artisan schedule:run" timing: "* * * * *" @@ -27406,12 +27027,12 @@ zerops: ``` Each service configuration requires a `run` section. Optional `build` and `deploy` sections can be added to further customize your process. ## Service Configuration -### setup +### setup Contains the hostname of your service (must exist in Zerops). ```yaml setup: app ``` -### extends +### extends The `extends` key allows you to inherit configuration from another service defined in the same `zerops.yaml` file. This is useful for creating environment-specific configurations while maintaining a common base. ```yaml zerops: @@ -27441,8 +27062,8 @@ When using `extends`: :::tip Create a base service with common configuration and extend it for environment-specific services to keep your `zerops.yaml` file DRY (Don't Repeat Yourself). ::: -## Build Configuration -### base +## Build Configuration +### base Sets the base technology for the build environment. [See available options](/zerops-yaml/base-list). ```yaml build: @@ -27456,7 +27077,7 @@ build: prepareCommands: - zsc add python@3.9 ``` -### os +### os Sets the operating system for the build environment. Options: - `alpine` (default) - `ubuntu` (default for ubuntu service) @@ -27467,7 +27088,7 @@ Current versions: build: os: ubuntu ``` -### prepareCommands +### prepareCommands Customizes the build environment by installing additional dependencies or tools. ```yaml build: @@ -27478,7 +27099,7 @@ build: :::note `build.prepareCommands` run in the `/home/zerops` directory. ::: -### buildCommands +### buildCommands Defines the commands to build your application. ```yaml build: @@ -27496,7 +27117,7 @@ buildCommands: npm install npm run build ``` -### deployFiles +### deployFiles Specifies which files or folders to deploy after a successful build. ```yaml build: @@ -27540,16 +27161,16 @@ This example above ignores `file.txt` in ANY directory named `src`, such as: :::note `.deployignore` file also works with `zcli service deploy` command. ::: -### cache +### cache Defines which files or folders to cache for subsequent builds. ```yaml build: cache: node_modules ``` For more information, see our detailed [guide on build cache](/features/build-cache), complete with extensive examples. -### addToRunPrepare +### addToRunPrepare Defines files or folders to be copied from the build container to the prepare runtime container. -### envVariables +### envVariables Sets environment variables for the build environment. ```yaml build: @@ -27562,8 +27183,8 @@ build: :::info The `yamlPreprocessor` option in your project & service import YAML allows you to generate random secret values, passwords, and public/private key pairs. For more information, see the [yamlPreprocessor](/references/import-yaml/pre-processor) page. ::: -## Deploy Configuration -### temporaryShutdown +## Deploy Configuration +### temporaryShutdown Controls the container replacement order during deployment. ```yaml deploy: @@ -27573,7 +27194,7 @@ deploy: - Default: `false` **When `false` (default):** New containers are started before old containers are removed, ensuring zero-downtime deployment. **When `true`:** Old containers are removed before new containers are started, causing temporary downtime but using fewer resources during deployment. -### readinessCheck +### readinessCheck Defines a readiness check for your application. Requires either `httpGet` object or `exec` object. ```yaml deploy: @@ -27592,23 +27213,23 @@ Readiness checks work similarly to [health checks](#healthcheck-) but are specif Available parameters: #### httpGet and exec The `httpGet` and `exec` options work the same way as in [health checks](#healthcheck-). See that section for detailed parameter descriptions. -#### Common parameters +#### Common parameters The following parameters can be used with either `httpGet` or `exec` readiness checks: - **failureTimeout** - Time in seconds until container is marked as failed. - **retryPeriod** - Time interval in seconds between readiness check attempts (equivalent to `execPeriod` in health checks). :::tip Unlike health checks which run continuously, readiness checks only run during deployments to determine when your application is ready to accept traffic. ::: -## Runtime Configuration -### base +## Runtime Configuration +### base Sets the base technology for the runtime environment. If not specified, the current version is maintained. ```yaml run: base: nodejs@latest ``` -### os +### os Sets the operating system for the runtime environment. Options and versions are the same as for the build environment. -### ports +### ports Specifies the internal ports on which your application will listen. ```yaml run: @@ -27620,23 +27241,23 @@ run: ... ``` Available parameters: -#### port +#### port Defines the port number on which your application listens. Must be between *10* and *65435*, as ports outside this range are reserved for internal Zerops systems. -#### protocol +#### protocol Specifies the network protocol to use: - Allowed values: `TCP` *(default)* or `UDP` -#### httpSupport +#### httpSupport Indicates whether the port is running a web server: - Default value: `false` - Set to `true` if a web server is running on the port - Only available with TCP protocol - Used by Zerops for [public access](/features/access) configuration -### prepareCommands +### prepareCommands Customizes the runtime environment by installing additional dependencies or tools. :::note `run.prepareCommands` run in the `/home/zerops` directory. ::: -### initCommands +### initCommands Defines commands to run each time a new runtime container starts or restarts. ```yaml run: @@ -27646,13 +27267,13 @@ run: :::note `run.initCommands` run in the `/var/www` directory. ::: -### start +### start Defines the start command for your application. ```yaml run: start: npm start ``` -### startCommands +### startCommands Defines start commands. Unlike `start`, you can define multiple commands that starts their own processes. ```yaml @@ -27669,11 +27290,11 @@ run: - litestream restore -if-replica-exists -if-db-not-exists -config=litestream.yaml $DB_NAME ``` See [start-commands-example](https://github.com/zeropsio/start-commands-example) -### documentRoot +### documentRoot Customizes the root folder for publicly accessible web server content (available only for webserver runtimes). -### siteConfigPath +### siteConfigPath Sets the custom webserver configuration (available only for webserver runtimes). -### envVariables +### envVariables Defines environment variables for the runtime environment. ```yaml run: @@ -27684,7 +27305,7 @@ Defines environment variables for the runtime environment. DB_USER: db DB_PASS: ${db_password} ``` -### envReplace +### envReplace Automatically replaces environment variable placeholders in your static files with their actual values during deployment. ```yaml run: @@ -27696,11 +27317,11 @@ run: - ./config/ ``` Available parameters: -#### delimiter +#### delimiter Characters that wrap your variable names in placeholders (e.g., `%%` means placeholders look like `%%VARIABLE%%`). - Type: `string` or `array of strings` - Supports multiple delimiters simultaneously -#### target +#### target Files or directories to process for variable replacement. - Type: `string` or `array of strings` - Can be specific files or directories @@ -27733,7 +27354,7 @@ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... -----END PUBLIC KEY----- ``` The placeholder gets replaced with the actual JWT public key during deployment. -### routing +### routing Configures URL routing, redirects, and HTTP headers (only for Static services). ```yaml run: @@ -27750,15 +27371,15 @@ run: X-Frame-Options: "'DENY'" ``` Available parameters: -#### root +#### root Sets a custom root directory for the service. - Type: `string` -#### cors +#### cors Enables CORS headers for cross-origin requests. - Type: `string` - Sets `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, `Access-Control-Allow-Headers`, and `Access-Control-Expose-Headers` - Special case: `"*"` is automatically converted to `'*'` -#### redirects +#### redirects Defines URL redirects and rewrites. - Type: `array of objects` - Each redirect object supports: @@ -27767,7 +27388,7 @@ Defines URL redirects and rewrites. - **status** - HTTP status code (required for absolute URLs) - **preservePath** - Preserve path after wildcard match - **preserveQuery** - Preserve query parameters -#### headers +#### headers Sets custom HTTP headers for specific paths. - Type: `array of objects` - Each header object supports: @@ -27794,7 +27415,7 @@ run: X-Frame-Options: "'DENY'" Content-Security-Policy: '"default-src ''self''"' ``` -### healthCheck +### healthCheck Defines a health check for your application. ```yaml run: @@ -27817,16 +27438,16 @@ run: execPeriod: 10 ``` Available parameters: -#### httpGet +#### httpGet Configures the health check to request a local URL using a HTTP GET method. - **port** - Defines the port of the HTTP GET request. - **path** - Defines the URL path of the HTTP GET request. - **host** - The health check is triggered from inside of your runtime container so it uses the localhost (127.0.0.1). If you need to add a host to the request header, specify it in the host attribute. - **scheme** - The health check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: `https`. -#### exec +#### exec Configures the health check to run a local command. - **command** - Defines a local command to be run. The command has access to the same environment variables. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example above. -#### Common parameters +#### Common parameters The following parameters can be used with either `httpGet` or `exec` health checks: - **failureTimeout** - Time in seconds until container fails after consecutive health check failures (reset by success). - **disconnectTimeout** - Time in seconds until container is disconnected and becomes publicly unavailable. @@ -27835,7 +27456,7 @@ The following parameters can be used with either `httpGet` or `exec` health chec :::tip Health checks continuously monitor your running application, while readiness checks verify if a new deployment is ready to receive traffic. For readiness checks, see the [readinessCheck section](#readinesscheck-). ::: -### crontab +### crontab Defines scheduled commands to run as cron jobs within a service. ```yaml run: diff --git a/apps/docs/static/llms-small.txt b/apps/docs/static/llms-small.txt index e1d1d9fbc..b62097d7e 100644 --- a/apps/docs/static/llms-small.txt +++ b/apps/docs/static/llms-small.txt @@ -20,7 +20,7 @@ zerops: - curl something else # OPTIONAL. Build your application buildCommands: - - + - # REQUIRED. Select which files / folders to deploy after # the build has successfully finished deployFiles: app @@ -155,7 +155,7 @@ zerops: base: alpine@3.20 # OPTIONAL. Build your application buildCommands: - - + - ... ``` Build commands are optional. Zerops triggers each command in the defined order in a dedicated build container, running from the `/build/source` directory. @@ -334,19 +334,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to an Alpine service with hostname = "app" and port = 8080 from another service of the same project, simply use `app:8080`. Read more about [how to access an Alpine service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the Alpine runtime environment by installing additional dependencies or tools to the runtime base environment. The base Alpine environment contains {data.alpine.default}, Zerops command line tool and `git` and `wget`. To install additional packages or tools add one or more prepare commands: @@ -470,25 +465,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -510,16 +499,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Alpine application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -562,25 +547,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -603,16 +582,12 @@ Read more about how the [readiness check works](/alpine/how-to/deploy-process#re #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Alpine application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -637,7 +612,6 @@ Read more about how the [readiness check works](/alpine/how-to/deploy-process#re # Alpine > How To > Build Process - ## Build process overview Zerops starts a temporary build container and performs the following actions: 1. **Installs the build environment** - Sets up base system and runtime @@ -654,7 +628,7 @@ Configure your build process in your `zerops.yaml` file according to the full b The default build environment contains: - {data.alpine.default} - [zCLI](/references/cli), Zerops command line tool -- +- ### Customize build environment To install additional packages or tools, add one or more build.prepareCommands to your `zerops.yaml`. :::info @@ -662,23 +636,18 @@ The application code is available in the `/build/source` folder in your build co ::: ### Build hardware resources All runtime services use the same hardware resources for build containers: - HW resource Minimum Maximum - CPU cores 1 5 - RAM 8 GB 8 GB - Disk 1 GB 100 GB - Build containers start with minimum resources and scale vertically up to maximum capacity as needed. ### Build time limit The time limit for the whole build pipeline is **1 hour**. After 1 hour, Zerops will terminate the build pipeline and delete the build container. @@ -825,70 +794,39 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description - hostname - The unique service identifier. - The hostname of the new database will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Alpine service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -904,7 +842,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Alpine service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -965,7 +902,7 @@ It is also a great option when you need a specific version of a technology (like The default runtime environment contains: - {data.alpine.default} - [zCLI](/references/cli) -- +- ### When You Need a Custom Runtime Image Since Alpine serves as a general-purpose base, you'll likely want to customize it for your specific use case. Common scenarios include: :::important @@ -1567,25 +1504,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -1862,67 +1793,39 @@ The `project:` section is required. Only one project can be defined. | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Bun and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Bun service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -1938,7 +1841,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Bun service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -2046,7 +1948,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-bun), a **_recipe_**, containing the most simple Bun web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Bun app running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-bun/blob/main/zerops-project-import.yaml)): ```yaml @@ -2753,25 +2654,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -2920,7 +2815,6 @@ Read more about how the [readiness check works](/deno/how-to/deploy-process#read # Deno > How To > Build Process - ## Build process overview Zerops starts a temporary build container and performs the following actions: 1. **Installs the build environment** - Sets up base system and Deno runtime @@ -2946,23 +2840,18 @@ The application code is available in the `/build/source` folder in your build co ::: ### Build hardware resources All runtime services use the same hardware resources for build containers: - HW resource Minimum Maximum - CPU cores 1 5 - RAM 8 GB 8 GB - Disk 1 GB 100 GB - Build containers start with minimum resources and scale vertically up to maximum capacity as needed. :::info Build container resources are not charged separately. Limited build time is included in your [project core plan](/company/pricing#project-core-plans), with additional build time available if needed. @@ -3126,68 +3015,40 @@ The `project:` section is required. Only one project can be defined. | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Deno and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Deno service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -3203,7 +3064,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Deno service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -3268,7 +3128,6 @@ If your Deno application needs more than what's included in the default environm - **Native dependencies**: When your Deno modules require system libraries that aren't in the default environment Here are Deno-specific examples of configuring custom runtime images in your `zerops.yml`: ### Basic Deno Setup - ### Using Build Files in Runtime Preparation ```yaml build: @@ -3354,7 +3213,6 @@ Have you got any additional question? Join our **[Discord](https://discord.com/i As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-deno), a **_recipe_**, containing the most simple Deno web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Deno app running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-deno/blob/main/zerops-project-import.yaml)): ```yaml @@ -3418,7 +3276,6 @@ Despite these limitations, Docker services offer some benefits: ## Configuration Guide ### Supported Version Currently supported Docker versions: -Import configuration version: ### Basic Structure Docker services in Zerops are configured through the `zerops.yaml` file. Here's a typical configuration pattern: ```yaml title="zerops.yaml" @@ -3430,7 +3287,7 @@ zerops: - docker image pull : # Always use specific version tags start: docker run --network=host : ports: - - port: + - port: httpSupport: true ``` :::important @@ -3981,19 +3838,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a .NET service with hostname = "app" and port = 5000 from another service of the same project, simply use `app:5000`. Read more about [how to access a .NET service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the .NET runtime environment by installing additional dependencies or tools to the runtime base environment. The base .NET environment contains {data.alpine.default}, the selected @@ -4122,25 +3974,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -4162,16 +4008,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your .NET application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -4214,25 +4056,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -4255,16 +4091,12 @@ Read more about how the [readiness check works](/dotnet/how-to/deploy-process#re #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your .NET application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -4410,85 +4242,50 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains .NET and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - The hostname of the new database will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. See what [.NET service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -4504,7 +4301,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add .NET service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -4612,7 +4408,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-dotnet-hello-world), a **_recipe_**, containing the most simple .NET web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of .NET running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-dotnet-hello-world/blob/main/import-project/description.yaml)): ```yaml @@ -5229,25 +5024,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -5524,68 +5313,40 @@ The `project:` section is required. Only one project can be defined. | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Elixir and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Elixir service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -5601,7 +5362,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Elixir service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -5709,7 +5469,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-elixir), a **_recipe_**, containing the most simple Elixir web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Elixir app running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-elixir/blob/main/zerops-project-import.yaml)): ```yaml @@ -5793,7 +5552,7 @@ See the [VPN reference guide](/references/networking/vpn). Connect securely to your project's internal network from your local machine: ```bash # Connect to your project -zcli vpn up +zcli vpn up # Access services using internal hostnames curl http://api:3000/health # Disconnect when done @@ -5842,7 +5601,6 @@ From this section, you can: - Create a one-time backup - Change the frequency/disable of automatic backups - Configure retention policies and limits - ### Backup Frequency Options Available schedules: - **No backups**: Disable automatic backups (not recommended) @@ -5851,25 +5609,18 @@ Available schedules: - **Once a month**: Monthly backups on a specific day and time - **Custom CRON**: Define a custom schedule using CRON syntax For the Custom CRON option, you can use the following syntax: - Field name Allowed values - Minute 0-59 - Hour 0-23 - Day 1-31 - Month 1-12 - Week Day 0–7; both 0 and 7 represent Sunday - Examples: - `0 2 * * *` - Every day at 2:00 AM - `0 4 * * 0` - Every Sunday at 4:00 AM @@ -5896,7 +5647,6 @@ In this section, you can: - View all backups with their timestamps and sizes - Download backups - Delete backups - :::note When creating manual backups via the UI, you'll see immediate feedback. If the backup takes longer than 10 seconds, the process continues in the background. You can verify completion by refreshing the backup list or checking service logs. ::: @@ -6217,35 +5967,27 @@ Zerops CDN is a global content delivery network that brings your static content - **Simple Integration**: No complex configuration required ## Global CDN Infrastructure Zerops CDN operates across **6 strategic regions** to ensure your content is always delivered from a location close to your users: - Region Location Coverage Area - EU CZ Prague, Czech Republic Primary European coverage + failover for all regions - DE Falkenstein, Germany - UK London, United Kingdom UK and surrounding areas - AU Sydney, Australia Australia and Oceania - SG Singapore, Singapore Southeast Asia - CA Beauharnois, Canada North America - ### Geo-Steering Technology Zerops CDN's geo-steering technology automatically routes users to the server location closest to them. Here's how it works: * **Automatic routing**: Users are directed to the optimal CDN node based on their geographic location @@ -6347,27 +6089,21 @@ Zerops provides multiple ways to manage and purge cached content before its norm --header "Authorization: Bearer $USER_OR_ACCESS_TOKEN" ``` #### Purge Pattern Examples - Pattern Description Example - `/*` Purges all content Useful after major updates - `/images/*` Purges all content in a directory Clear all cached images - `/css/main.css$` Purges a specific file Update a single CSS file - `/2023*` Purges content starting with pattern Clear content with date prefix - :::warning Pattern Rules - Wildcards (`*`) must be at the end of the pattern - Specific files must include `$` at the end @@ -6385,7 +6121,7 @@ The endpoint links below will take you to the Swagger documentation with detaile ### Cache Purge API - **[Purge Storage Mode Cache ↗](https://api.app-prg1.zerops.io/api/rest/public/swagger/#/PublicServiceStack/PurgeStorageCdn)** `PUT /api/rest/public/service-stack/{id}/purge-cdn/{path}` - **[Purge Static Mode Cache ↗](https://api.app-prg1.zerops.io/api/rest/public/swagger/#/PublicProject/PurgeStaticCdn)** `PUT /api/rest/public/project/{id}/purge-cdn/static/{domain}/{path}` -- **Purge Api Mode Cache *(Coming soon)*** +- **Purge Api Mode Cache *(Coming soon)*** ## Troubleshooting Having issues with your CDN? Here are solutions to the most common problems: #### Content Not Updated After Changes @@ -6424,6 +6160,67 @@ Remember that only publicly accessible objects will be cached by the CDN. Privat ---------------------------------------- +# Features > Coding Agents + +Coding agents need current project knowledge and a safe way to act on it. The hard part is rarely "write the function" — it's letting the agent connect product intent to infrastructure: services, env vars, logs, deploys, verification, and a path to production. +**Zerops Control Plane (ZCP)** gives a coding agent current project state and bounded operations on one Zerops project. You describe the app or change you want; ZCP lets the agent discover services, use existing infrastructure or create what is missing, wire the app to managed services, deploy through the standard pipeline, verify the real endpoint, inspect logs, and report a URL or blocker. You hold the product intent, constraints, approvals, and quality bar. +:::caution Keep production in a separate project +ZCP runs with a project-scoped token that grants operational rights inside that project. Put it in a development or staging project, not the project serving production traffic. That keeps agent-driven changes away from production unless your CI or release process promotes them. See [Production boundary](/zcp/security/production-policy). +::: +## Who it's for +Developers using a coding agent on application work that needs real infrastructure. The developer stays focused on the product outcome, technology choices, acceptance criteria, and judgment. ZCP gives the agent the platform context and operations needed to use or create services, connect the app to infrastructure, deploy, verify, recover from failure, and report back. +The **Include Coding Agent** option bundles the currently supported agent CLI shown in the dashboard. Local setup can connect a compatible local agent client after `zcp init`. +## What ZCP lets the agent do +ZCP exposes a fixed set of operations on one Zerops project, grouped by user job. The prompt can stay focused on the app because the agent does not need you to describe what the project looks like - ZCP reports it from what is 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 | [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) | +| **Verify** | Reachability + behavior checks against the actual URL | [Deploy, verify, and fix](/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) | +| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | +| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | +Behind one app request, the agent should read the project, use existing services or create missing ones, edit and deploy the app, verify the requested behavior, and then record how future changes should ship. A green deploy with a 500 on the relevant route is not done — verification has two layers and both must pass. +Full workflow terms: [Workflow terms](/zcp/reference/agent-workflow). +## Two ways to run it +The `zcp` binary can run in a Zerops service or on your laptop. The user choice is remote setup or local setup. + A Zerops service (`zcp@1`) inside the project runs ZCP. Enable **Include Coding Agent** to add the bundled agent CLI and preconfigure it to use ZCP. Enable Cloud IDE when you want browser VS Code. **Recommended starting point.** [Set up →](/zcp/setup/hosted-workspace) + The `zcp` binary on your laptop, initialized in a project folder with `zcp init`. The agent runs in your local editor or CLI; private service access uses `zcli vpn up`. [Set up →](/zcp/setup/local-agent-bridge) +Either way, the agent gets the same project-scoped ZCP operations against the same project. Remote setup can package ZCP with an agent CLI and browser editor; local setup connects your own agent client to ZCP. +Decision: [Choose remote or local setup](/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 remote setup adds +Remote setup starts with a `zcp@1` Zerops service. Two dashboard options decide what is bundled into that service: +**Include Coding Agent.** Adds the currently supported bundled agent CLI and preconfigures it to use ZCP running inside the `zcp@1` service. Agent authentication is handled separately by that agent's login method. +**Cloud IDE.** Browser-based VS Code (code-server) with 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 → Cloud IDE](/features/local-remote-development#cloud-ide-on-remote-setup). +Without **Include Coding Agent**, the service can still exist, but there is no preconfigured agent waiting inside it. Without **Cloud IDE**, there is no browser editor. With both enabled, you get a one-click agent + IDE setup inside the project. +Both options are configured when you provision the service: [Set up remote ZCP → Path B](/zcp/setup/hosted-workspace#path-b--toggle-the-zcp-service). +## 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. +ZCP is the ergonomic layer on top of Zerops APIs, `zcli`, and SSH for agents. It tightens the loop by packaging project state, guidance, operations, and verification behind one project-scoped interface. +## Operations and permissions +ZCP groups its operations into three categories so an agent client or team policy can gate them: + Inspect state, logs, events, configuration, and verification output. Safe to auto-allow. [Full list →](/zcp/reference/mcp-operations#read-only-operations) + Deploy, change env vars, manage lifecycle, scale, or delete services. Require team policy and confirmation where needed. [Full list →](/zcp/reference/mcp-operations#mutating-operations) + Set up infrastructure, mounts, imports, work sessions, or delivery configuration. Gated by team policy. [Full list →](/zcp/reference/mcp-operations#operational-setup) +That's the surface. The boundary is the project. The token authorizes one project, full stop — cross-project, account-wide, or organization actions stay outside reach. Integration tokens are still bounded by the permissions of the user who created them and by the project access selected for the token; see [Roles & Permissions](/features/rbac). Full blast-radius framing: [Trust model](/zcp/security/trust-model). +Full operation list and permission semantics: [Advanced operations](/zcp/reference/mcp-operations). Token shape and confirmation gates: [Tokens and credentials](/zcp/security/tokens-and-project-access). +## Customize remote setup +Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product - install packages, run additional processes alongside the agent, or ship a hardened version with team-standard tools baked in. Patterns: [Customize remote setup](/zcp/setup/hosted-workspace#customize-remote-setup). +Local setup isn't extensible the same way; the binary on your laptop is just ZCP - editor, shell, and dev server stay your tools' job. +## Source control +When the agent runs in remote setup, 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. In local setup, 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). +## What ZCP is not +ZCP is not a prompt-to-prototype generator, an isolated code sandbox, or an editor-only cloud development environment. It is the control plane that gives a coding agent project-scoped infrastructure operations on Zerops: services, env vars, deploys, logs, verification, recovery, and delivery handoff. +## Where to start + +---------------------------------------- + # Features > Container Vs Vm Ever wondered why container technologies like Docker took over the development world so quickly? Let's break down the real differences between traditional VMs and containers - and why you might want to use one over the other. @@ -6457,27 +6254,16 @@ Think of it like this: At Zerops, we use **containers** as our primary runtime environment - they're fast, efficient, and perfect for most modern development workflows. We've optimized our container infrastructure to handle nearly every type of application you might need to run. However, we also provide **VMs** when you need them, particularly for Docker-based workloads where the additional isolation is essential. Docker containers are a special case - in Zerops, they actually need to run inside VMs for proper security and isolation. While it's technically possible to run Docker in containers using privileged mode, this creates security vulnerabilities. ### When to Use What - Go with Containers when: - Building modern web applications - Working with microservices - Need quick deployment and vertical scaling - Want efficient resource usage - Consider VMs when: - Running legacy applications - Need complete OS isolation - Require specific hardware access - Need to run Docker containers - ### Resource Allocation Both containers and VMs in Zerops can have guaranteed resources: - Specific CPU cores @@ -6511,7 +6297,6 @@ The entire build process, including any time spent in debug mode, has a maximum ::: ## Configuration The debug mode configuration can be found in your service detail under the **Pipelines & CI/CD settings**. - ## Debug Control When execution is paused in debug mode, you have several commands available to control the debugging process. Each command serves a specific purpose and affects the deployment process differently. ### Debug Pause Points @@ -6526,37 +6311,27 @@ To proceed with the normal deployment process, use: ```bash zsc debug continue ``` - Pause Point Behavior - ↪ Before First Command Begins running commands for the current phase until next possible pause point - ✖ On Command Failure Skips the failed command and continues deployment - ✔ After Last Command Moves to the next phase (from build to runtime prepare) or completes deployment - #### Marking Success To force a successful deployment status, use: ```bash zsc debug success ``` - Pause Point Behavior - ↪ Before First Command Ends current phase without running any commands - ✖ On Command Failure Ignores the failure and ends current phase with success - ✔ After Last Command Concludes current phase with a successful status - :::note Requires valid `deployFiles` to work properly (fails otherwise). ::: @@ -6565,41 +6340,30 @@ To terminate the deployment with a failure status, use: ```bash zsc debug fail ``` - Pause Point Behavior - ↪ Before First Command Marks current phase as failed without running commands - ✖ On Command Failure Ends deployment with original error - ✔ After Last Command Overwrites successful execution with failed status and ends deployment - Each phase can be configured independently to pause at any of the points described above, giving you precise control over your debugging workflow. The 60-minute timeout ensures deployments don't remain blocked indefinitely. ## Usage Examples ### Example 1: Debugging Build Failures - Build phase ✖ On Command Failure - Prepare runtime phase ➠ Disable - This configuration allows you to: 1. Inspect the container state after a failure 2. Make necessary adjustments 3. Use `zsc debug continue` to resume or `zsc debug fail` to abort ### Example 2: Validating Runtime Setup - Build phase ➠ Disable - Prepare runtime phase ✔ After Last Command - ## Best Practices #### Targeted Debugging - Enable debug mode only for the specific phase you need to investigate @@ -6661,7 +6425,6 @@ Navigate to service details and find **Environment variables** in the menu. You - Add individual variables using the "Add secret variable" button - Edit individual variables through the menu that appears on hover - Use the bulk editor for managing multiple variables in .env format - ##### Import Configuration Create secret variables for a service with `envSecrets` attribute. See the complete [import.yaml structure](/references/import). ```yaml title="import.yaml" @@ -6712,16 +6475,12 @@ A security feature that controls the **visibility** of environment variables acr By default, Zerops isolates environment variables between services to enhance security and prevent unintended access to sensitive information. This isolation can be configured at both project and service levels. ### Isolation Modes Zerops supports two isolation modes: - Mode Description - service Default mode. Variables are isolated to their respective services. Services can only access their own variables and must explicitly reference variables from other services. - none Legacy mode. All variables from all services are automatically shared and accessible via prefixing. - ### Configuring Isolation #### Project-Level Isolation Zerops automatically creates the `envIsolation` project variable with the default value `service`. You only need to modify this if you want to disable isolation: @@ -6887,38 +6646,27 @@ Enterprise-grade infrastructure with separated core services across multiple con Production applications, high-traffic websites, mission-critical business applications, teams requiring maximum uptime. ::: #### Features Comparison - Lightweight Core Serious Core - Infrastructure Single container (limited redundancy) Multi-container (highly available) - SSL Termination - Automatic Certificate Generation - Proxy / Load Balancer - IPv6 Address - Build Time 15 hours 150 hours - Backup Space 5 GB 25 GB - Egress 100 GB 3 TB - Failover Protection Limited Comprehensive - For detailed pricing information on both core types, visit our [pricing page](/company/pricing#project-core-plans). #### Project Core Upgrade You can upgrade from Lightweight Core to Serious Core for enhanced reliability and increased resources. @@ -6957,6 +6705,103 @@ Containers are the most granular level of the Zerops architecture. Each service ---------------------------------------- +# Features > Local Remote Development + +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. Across local VPN, Cloud IDE, and SSH, you use the same project network, hostnames, service types, and deploy pipeline. +This is also what narrows the gap between development and production. Development and staging can use the same Zerops-managed service types, networking model, and `zerops.yaml` patterns production uses. Production should still live in a separate project with separate credentials, access rules, and resource policies. +:::note +Remote setup is a `zcp@1` service you can add to any project. It can provide a Linux dev container with a curated toolchain, optional Cloud IDE, and optional **Include Coding Agent** wiring. The modes below can use that service, connect directly to runtime services, or stay fully local over VPN; local development with `zcli vpn up` does not 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. Remote setup isn't required. +- **Cloud IDE.** 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 the `zcp` service, or directly to a service container. +Same `db:5432`, same `api:3000`, same project credentials in all three. Switch modes without changing anything about the development 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: add remote setup, 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 service type as stage or production. +This mode is also where coding agents running on your laptop fit. Without ZCP, a local agent client inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. To let the agent operate the project - discover, deploy, verify, read logs, and manage delivery handoff - set up [local ZCP](/zcp/setup/local-agent-bridge). +**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). +::: +## Cloud IDE on remote setup +**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 remote setup (a `zcp@1` service) to a project — see [Set up remote ZCP](/zcp/setup/hosted-workspace) — you get an Ubuntu-based dev container with `zcp`, `zcli`, GitHub CLI, database CLIs, shell utilities, browser automation tools, and SSHFS. Enable **Cloud IDE** for browser-based VS Code; enable **Include Coding Agent** for the bundled agent CLI plus ZCP wiring. +Remote setup can also mount 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 Zerops deploy pipeline. One remote setup service 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.** Remote 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 the `zcp` service's secret environment variables; `git` reads it through a credential helper. +Either pattern keeps your token in the `zcp` service, 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 the `zcp` service +``` +In this mode, remote setup 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. Remote setup becomes a *convenience* layer: a single container that carries the toolchain, mounts multiple dev services over SSHFS, and can include `zcli`, Cloud IDE, and coding-agent wiring for the project. If you'd rather connect your editor directly to your runtime services and skip remote setup 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 the `zcp` service) 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 | Native IDE over SSH | +|---|---|---|---| +| Editor runs | Local | Browser | Local | +| Toolchain | Local | Remote ZCP | Remote ZCP or service container | +| Dev databases | Hostname via VPN | Native, on private network | Native, on private network | +| Remote ZCP 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 | +| 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 remote setup are hitting the same `db:5432`. +For the agent-driven path, the setup decision (remote `zcp@1` service vs. local `zcp` binary) is on its own page: [Choose remote or local setup](/zcp/setup/choose-workspace). +## Same architecture, separate production +The development environments above don't approximate production by inventing a different platform. A ZCP development project can run dev and stage services with the same Zerops-managed service types, private networking, load-balancer model, and `zerops.yaml` patterns that production uses in its separate project. Dev, stage, and production differ in resource allocation, access rules, and credentials, 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 and stage, you've already tested it against the kind of infrastructure it'll meet in production. The remaining differences are scale, credentials, access policy, and data. +This applies whether the developer is human or an agent — see [ZCP for Coding Agents](/features/coding-agents) for the agent case. +## How this differs from cloud IDEs +If you've used GitHub Codespaces or Gitpod, Cloud IDE 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 use the same platform model.** 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 model: managed Postgres, private networking, and `zerops.yaml`. Production still lives in its own project with its own credentials and policies. +Remote ZCP 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 +- 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 → [ZCP for Coding Agents](/features/coding-agents) +- Set up remote setup → [Set up remote ZCP](/zcp/setup/hosted-workspace) +- Pick remote or local ZCP → [Choose remote or local setup](/zcp/setup/choose-workspace) +- Term reference → [ZCP Glossary](/zcp/glossary) + +---------------------------------------- + # Features > Pipeline export const languages = [ @@ -7027,13 +6872,11 @@ The zerops.yaml in your repository tells Zerops how to build and deploy your app 6. Start your application using `npm start` Learn more about `zerops.yaml` parameters for your runtime: ## Trigger the pipeline - ### Continuous deployment Set up automatic builds triggered by Git events. You can establish continuous deployment in two ways: * **New Service:** Create a new runtime service and connect it to your GitHub or GitLab repository during the service creation process. * **Existing Services:** Go to the service detail and choose **Pipelines & CI/CD settings** from the left menu. Click **Connect with a GitHub repository** or **Connect with a GitLab repository** to link your repository. Once connected, Zerops will automatically build and deploy your application with each push to the selected branch or when you create a new tag. - ### On-demand deployment Trigger builds and deployments manually when needed using either the CLI or GUI. #### Using Zerops CLI @@ -7047,7 +6890,6 @@ In **Pipelines & CI/CD settings** section of your service detail: #### Using import YAML Add `buildFromGit: ` to your service configuration for one-time build during import. See [import documentation](/references/import#service-basic-configuration). ## Build phase - Zerops starts a temporary build container and executes these steps: 1. **Install build environment** - Sets up the runtime and tools 2. **Download source code** - From [GitHub ↗](https://www.github.com), [GitLab ↗](https://www.gitlab.com) or via [Zerops CLI](/references/cli) @@ -7058,23 +6900,18 @@ Zerops starts a temporary build container and executes these steps: Zerops automatically deletes the build container after the build finishes or fails. ### Build hardware resources All runtime services use the same hardware resources for build containers: - HW resource Minimum Maximum - CPU cores 1 5 - RAM 8 GB 8 GB - Disk 1 GB 100 GB - Build containers start with minimum resources and scale vertically up to maximum capacity as needed. :::info Build container resources are not charged. Build costs are covered by the standard Zerops [project fee](https://zerops.io/#pricing). @@ -7085,7 +6922,6 @@ The entire build pipeline has a **1 hour** time limit. After 1 hour, Zerops term All runtime services start with a default build environment based on the [build.base](/zerops-yaml/specification#base-) attribute in `zerops.yaml`. Install additional packages or tools by adding [build.prepareCommands](/zerops-yaml/specification#preparecommands-) to your configuration. Learn more about customizing build environments: ## Runtime prepare phase (optional) - When your application requires additional system packages, libraries, or tools in the runtime environment, Zerops allows you to build a custom runtime image. This optional phase occurs after the build phase and before deployment. ### When to use custom runtime images Build custom runtime images when you need: @@ -7148,7 +6984,6 @@ Do not include your application code in the custom runtime image, as your built Shared storage mounts are also not available during the runtime prepare phase. ::: ## Deploy phase - ### Application artifacts After the [build phase](#build-phase) completes, Zerops stores the application artifact in internal storage and deletes the build container. For [manual deployments](#manual-deploy-using-zerops-cli) using Zerops CLI, the application artifact is also uploaded to internal storage. @@ -7190,7 +7025,6 @@ If readiness checks fail for 5 minutes, Zerops marks the container as failed, de - `exec.command` - Succeeds when command returns status code 0 (5-second timeout) Read the [runtime log](/nodejs/how-to/logs#runtime-log) to troubleshoot failed readiness checks. ## Manual deploy using Zerops CLI - Start deploy-only pipelines using the [Zerops CLI](/references/cli). The `zcli service deploy` command uploads and deploys your application in Zerops. Use this when you have your own build process. For building applications in Zerops, use [continuous](#continuous-deployment) or [on-demand](#on-demand-deployment) deployment instead. ```sh Usage: @@ -7218,14 +7052,12 @@ You can modify the deploy pipeline anytime by updating the `zerops.yaml` in your ## Manage builds and deployments ### Cancel running build When you need to cancel an incorrect running build, use the Zerops GUI. Go to the service detail, open the running processes list, and click **Open pipeline detail**. Then click **Cancel build**. - :::caution Build cancellation is only available before the build pipeline finishes. Once the build completes, deployment cannot be cancelled. ::: ### Application versions Zerops keeps the 10 most recent versions of your application in internal storage. Access the application versions list in Zerops GUI by going to service detail and choosing the **Pipelines & CI/CD settings** section from the left menu. The active version is highlighted - click the button below to show all archived versions. - Access pipeline details from the additional menu. Pipeline details contain: - Pipeline configuration (`zerops.yaml`) used for the selected version - Build log (if available) @@ -7248,37 +7080,31 @@ The primary role assigned to a user, determining their **default permissions** a An optional per-project permission that **supersedes the organization role** for that specific project and its resources (services, containers, environment variables, routing, etc.). ## User Roles Zerops supports four user roles, listed from highest to lowest permission level: - Role Organization Access Default Project Access Can Create Projects Can Manage Team - Owner Full (incl. billing) Full ✓ All roles - Admin Full (excl. billing) Full ✓ Developer, Guest - Developer None None (per-project only) ✓ ✗ - Guest None None (per-project only) ✗ ✗ - ### Owner :::note Every organization must have at least one Owner at all times. @@ -7376,48 +7202,37 @@ A Guest user (perhaps an external contractor) can be granted Read only or Full a :::note Owners and Admins cannot be set to "No access" on any project — the minimum override available for these roles is Read only. ::: - Organization Role Without Override With Full Access Override With Read Only Override - Owner Full access Full access Read only - Admin Full access Full access Read only - Developer No access Full access Read only - Guest No access Full access Read only - ## Integration Tokens Integration tokens provide programmatic access to the Zerops API, ideal for CI/CD pipelines, automation scripts, and third-party integrations. ### Token Access Levels - Token Type Description - Full access to all projects Can perform all operations on all current and future projects - Read access to all projects Can view all projects and their resources, but cannot make changes - Custom access per project No default access; each project must be explicitly added with Full or Read only permission - ### Creating Tokens Access token management at **Settings → Access Tokens Management**. 1. Click **Create Token** @@ -7430,22 +7245,16 @@ The token value is displayed only once upon creation. Store it securely — you ::: ### Token Permission Constraints Tokens cannot have higher permissions than the user who creates them: - Your Role Available Token Types - Owner Full access, Read access, Custom per project - Admin Full access, Read access, Custom per project - Developer Custom per project only - Guest Custom per project only - ### Managing Tokens :::note Admins can manage tokens created by Developers and Guests, but cannot manage tokens created by other Admins or Owners. @@ -7462,35 +7271,27 @@ For complete API documentation, see the [Zerops OpenAPI Specification](/referenc ::: ### Backend Role System The API uses a more granular role system than the dashboard interface: - Dashboard Role API Role Code Notes - Owner OWNER Includes canViewFinances, canEditFinances, and canCreateProjects flags (always enabled) - Admin ADMIN Includes canCreateProjects flag (always enabled) - Developer NO_ACCESS With canCreateProjects: true - Guest NO_ACCESS With canCreateProjects: false - — BASIC_USER Available via API only - — READ_ONLY Available via API only - ### Additional Roles (API Only) **BASIC_USER** - Can view all projects (unless overridden to `NO_ACCESS`) @@ -7506,19 +7307,14 @@ The API uses a more granular role system than the dashboard interface: - Sensitive environment variables displayed as `REDACTED` ### Permission Flags The API supports additional permission flags that can be set on any user regardless of role: - Flag Description - canViewFinances Allows viewing billing information, invoices, and payment sources - canEditFinances Allows modifying billing information and processing payments (automatically enables canViewFinances) - canCreateProjects Allows creating new projects (user receives OWNER role on projects they create) - These flags enable scenarios like: - An Admin who needs to view (but not edit) billing information - A Developer who should be able to create projects but also view invoices @@ -7577,31 +7373,26 @@ Docker services do not support automatic vertical scaling. Resource values can b - Managed by Zerops (no application changes needed) ### At-a-Glance Comparison * ✓ = Available *(configurable, defaults vary according to service type)* - Feature Runtime Services & Linux Containers Databases Shared Storage Docker - Automatic Resource Scaling ✓ ✓ ✓ Manual (triggers VM restart) - Automatic Horizontal Scaling ✓ Fixed # of containers Fixed # of containers ✓ - High Availability User-implemented Zerops-managed HA mode Zerops-managed HA mode User-implemented - ## When to Configure Scaling You can configure scaling settings at three different stages: - **During service creation** - Configure initial scaling parameters when creating services in the Zerops GUI. Set resource limits, CPU mode, and container counts from the start. @@ -7682,47 +7473,38 @@ Below are the parameters that control this behavior across all services that sup - **Scale-Down Threshold Percentile:** The usage percentile that triggers resource scaling down - **Minimum Step:** The smallest increment by which resources can increase during scaling - **Maximum Step:** The largest possible increment for resources when scaling rapidly under high load - Parameter CPU RAM Disk - Data Collection Interval 10 seconds 10 seconds 10 seconds - Scale-Up Window Interval 20 seconds 10 seconds 10 seconds - Scale-Down Window Interval 60 seconds 120 seconds 300 seconds - Scale-Up Threshold Percentile 60 50 50 - Scale-Down Threshold Percentile 40 50 50 - Minimum Step 1 (0.1 cores) 0.125 GB 0.5 GB - Maximum Step 40 32 GB 128 GB - ## Part 2: Container Architecture — Service-Specific Approaches Container architecture in Zerops defines how services are distributed across containers. Different service types use fundamentally different approaches: 1. **Horizontal Scaling** (Runtime Services, Linux Containers, and Docker) @@ -8341,25 +8123,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -8636,68 +8412,40 @@ The `project:` section is required. Only one project can be defined. | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | | **tags** | **Optional.** One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. | At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Gleam and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Gleam service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -8713,7 +8461,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Gleam service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -8821,7 +8568,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-gleam), a **_recipe_**, containing the most simple Gleam web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Gleam app running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-gleam/blob/main/zerops-project-import.yaml)): ```yaml @@ -9230,19 +8976,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a Go service with hostname = "app" and port = 8080 from another service of the same project, simply use `app:8080`. Read more about [how to access a Go service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the Go runtime environment by installing additional dependencies or tools to the runtime base environment. The base Go environment contains {data.alpine.default}, the selected @@ -9369,25 +9110,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -9409,16 +9144,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Go application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -9461,25 +9192,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -9502,16 +9227,12 @@ Read more about how the [readiness check works](/go/how-to/deploy-process#readin #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Go application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -9657,70 +9378,39 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description - hostname - The unique service identifier. - The hostname of the new database will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Go service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -9736,7 +9426,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Go service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -9844,7 +9533,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-go-hello-world), a **_recipe_**, containing the most simple Go web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Go running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-go-hello-world/blob/main/import-project/description.yaml)): ```yaml @@ -10102,22 +9790,27 @@ zsc cdn purge /style.css$ # Purge exact file # Guides > Choose Queue -**Use NATS** for most cases (simple, fast, JetStream persistence). Use **Kafka** only for enterprise event streaming with guaranteed ordering and unlimited retention. +**Use NATS** for most cases (simple, fast, optional JetStream persistence layer when durability is needed). Use **Kafka** only for enterprise event streaming with guaranteed ordering and unlimited retention. ## Decision Matrix | Need | Choice | Why | |------|--------|-----| -| **General messaging** | **NATS** (default) | Simple auth, JetStream built-in, fast | +| **General messaging** | **NATS** (default) | Simple auth, fast, JetStream available when needed | | Enterprise event streaming | Kafka | SASL auth, 3-broker HA, unlimited retention | -| Lightweight pub/sub | NATS | Low overhead, 8MB default messages | +| Lightweight pub/sub | NATS — core | Low overhead, 8MB default messages, fire-and-forget | +| Durable queues, replay, at-least-once | NATS — JetStream | Persistent streams, durable consumers, ack/redeliver | | Event sourcing / audit logs | Kafka | Indefinite topic retention, strong ordering | | Event sourcing / audit logs | Kafka | Indefinite topic retention, strong ordering | ## NATS (Default Choice) +NATS exposes **two distinct messaging shapes**. Pick ONE per recipe and write yaml comments / KB content describing only that shape — mixing them confuses porters about what the recipe actually does. +- **Core pub/sub + queue groups**: `nc.subscribe('subject', { queue: 'workers' })`. No persistence; queue groups load-balance delivery across replicas; lost messages stay lost. HA story: surviving cluster nodes keep delivering, no consumer position to restore. Use when fan-out + load balance + at-most-once is enough. +- **JetStream streams + durable consumers**: opens an explicit stream via `JetStreamManager`, subscribes durably via `js.subscribe(...)`. Persistent message store; replay on reconnect; ack/redeliver. HA story: cluster replicates stream state, acked-but-unprocessed messages survive node loss. Use when at-least-once + replay + persistence are required. +**Authoring rule**: a recipe's yaml comments and KB bullets should reflect the shape the code actually uses. If the worker only calls `nc.subscribe()` with a queue group and never opens a stream, do not invoke JetStream language at HA tiers — the recipe has no stream to replicate. If the worker opens a JetStream stream, the JetStream HA story is the relevant one. - Ports: 4222 (client), 8222 (HTTP monitoring) - Auth: user `zerops` + auto-generated password - **Connection** — two supported patterns, pick ONE: - **Separate env vars** (recommended, works with every NATS client library): pass `servers: ${hostname}:${port}` plus `user: ${user}, pass: ${password}` as client-side connect options. The servers list stays credential-free. - **Opaque connection string**: pass `${connectionString}` directly as the servers option — the platform builds a correctly-formatted URL with embedded auth that the NATS server expects. -- JetStream: Enabled by default (`JET_STREAM_ENABLED=1`) +- JetStream capability: enabled by default (`JET_STREAM_ENABLED=1`); recipes opt in by writing JetStream client code. Setting `JET_STREAM_ENABLED=0` hard-disables the capability across the project. - Storage: Up to 40GB memory + 250GB file store - Max message: 8MB default, 64MB max (`MAX_PAYLOAD`) - Health check: `GET /healthz` on port 8222 @@ -10133,7 +9826,7 @@ zsc cdn purge /style.css$ # Purge exact file ## Gotchas 1. **NATS config changes need restart**: No hot-reload — changing env vars requires service restart 2. **Kafka single-node has no replication**: 1 broker = 3 partitions but zero redundancy -3. **NATS JetStream HA sync interval**: 1-minute sync across nodes — brief data lag possible +3. **NATS JetStream HA sync interval**: 1-minute sync across nodes — brief data lag possible. Applies only to recipes that actually open JetStream streams; core pub/sub recipes are unaffected. 4. **Kafka SASL only**: No anonymous connections — always use the generated credentials 5. **NATS authorization violation from a hand-composed URL**: do not build a `nats://user:pass@host:4222` URL from the separate env vars. Most NATS client libraries will parse the embedded credentials AND separately attempt SASL with the same values, producing a double-auth that the server rejects with `Authorization Violation` on the first CONNECT frame (symptom: startup crash, no successful subscription). Use either the separate env vars passed as connect options (credential-free servers list) or the opaque `${connectionString}` the platform builds for you — both patterns in the Connection section above avoid the double-auth path. @@ -10239,7 +9932,7 @@ jobs: - uses: zeropsio/actions@main with: access-token: ${{ secrets.ZEROPS_TOKEN }} - service-id: + service-id: ``` - `access-token`: From Settings → Access Token Management - `service-id`: From service URL or three-dot menu → Copy Service ID @@ -10295,7 +9988,7 @@ Always use **Full (strict)** SSL mode in Cloudflare — "Flexible" causes redire ## DNS Configuration ### CNAME (non-apex or with CNAME flattening) ``` -CNAME +CNAME ``` ### With Cloudflare Proxy (orange cloud) | IP Type | Record | Proxy | @@ -10314,7 +10007,7 @@ CNAME ## Wildcard Domains ``` Method A: A *. + AAAA *. -Method B: CNAME *. +Method B: CNAME *. ACME: CNAME _acme-challenge. .zerops.zone ``` ## SSL/TLS Settings (Cloudflare Dashboard) @@ -10537,7 +10230,7 @@ run: db_hostname: ${db_hostname} # SELF-SHADOW — see next section db_password: ${db_password} # SELF-SHADOW queue_hostname: ${queue_hostname} # SELF-SHADOW - STAGE_API_URL: ${STAGE_API_URL} # SELF-SHADOW (project-level variant) + API_URL: ${API_URL} # SELF-SHADOW (project-level variant) ``` The referenced variable does **not** need to exist at definition time — Zerops resolves at container start. ### Self-Shadow Trap @@ -10549,7 +10242,7 @@ run: db_password: ${db_password} # OS env: db_password='${db_password}' (literal) ``` At runtime, the worker tries to connect to `"${db_hostname}:5432"` and crashes. The fix is to **delete the entire block** — those vars are already in the container's env without any declaration. -This applies identically to project-level vars (`${STAGE_API_URL}`, `${APP_SECRET}`) and cross-service vars (`${db_hostname}`, `${queue_user}`) — both auto-propagate, both self-shadow under the same rule. +This applies identically to project-level vars (`${API_URL}`, `${APP_SECRET}`) and cross-service vars (`${db_hostname}`, `${queue_user}`) — both auto-propagate, both self-shadow under the same rule. **Hostname transformation**: dashes become underscores. Service `my-db` variable `port` is `${my_db_port}`. ### Cross-Service References in API vs Runtime Cross-service references (`${hostname_varname}`) are **resolved at container start time**, not at definition time. This means: @@ -10578,26 +10271,26 @@ Project variables are **automatically available in every service, in both runtim ```yaml build: buildCommands: - - echo "building for $STAGE_API_URL" # shell reads the OS env var - - VITE_API_URL=$STAGE_API_URL npm run build # or pass it forward by shell prefix + - echo "building for $API_URL" # shell reads the OS env var + - VITE_API_URL=$API_URL npm run build # or pass it forward by shell prefix ``` **In `build.envVariables` YAML** (to compose a derived var that the bundler consumes) reference the project var directly without prefix: ```yaml build: envVariables: - VITE_API_URL: ${STAGE_API_URL} # project var STAGE_API_URL read as-is, NO RUNTIME_ prefix + VITE_API_URL: ${API_URL} # project var API_URL read as-is, NO RUNTIME_ prefix ``` **In `run.envVariables` YAML** (to forward a project var under a framework-conventional name without creating a shadow), reference directly without prefix: ```yaml run: envVariables: - FRONTEND_URL: ${STAGE_FRONTEND_URL} # project var STAGE_FRONTEND_URL forwarded as FRONTEND_URL + CORS_ALLOWED_ORIGIN: ${FRONTEND_URL} # project var FRONTEND_URL forwarded under a different name ``` **DO NOT** re-reference an auto-injected variable under its SAME name — that's a self-shadow loop. Applies to BOTH project-level vars AND cross-service vars: ```yaml envVariables: PROJECT_NAME: ${PROJECT_NAME} # project-level self-shadow - STAGE_API_URL: ${STAGE_API_URL} # project-level self-shadow + API_URL: ${API_URL} # project-level self-shadow db_hostname: ${db_hostname} # cross-service self-shadow queue_user: ${queue_user} # cross-service self-shadow ``` @@ -10613,10 +10306,10 @@ Dual-runtime recipes (frontend SPA + backend API on the same platform) use proje ```yaml project: envVariables: - STAGE_API_URL: https://apistage-${zeropsSubdomainHost}-3000.prg1.zerops.app - STAGE_FRONTEND_URL: https://appstage-${zeropsSubdomainHost}.prg1.zerops.app + API_URL: https://apistage-${zeropsSubdomainHost}-3000.prg1.zerops.app + FRONTEND_URL: https://appstage-${zeropsSubdomainHost}.prg1.zerops.app ``` -The platform resolves `${zeropsSubdomainHost}` when injecting the value into services at container start. The frontend consumes `STAGE_API_URL` via plain `${STAGE_API_URL}` in `build.envVariables` (baking it into the bundle at compile time) — **no `RUNTIME_` prefix**. The API consumes `STAGE_FRONTEND_URL` via plain `${STAGE_FRONTEND_URL}` in `run.envVariables` (for CORS allow-list). The same names must be set on the workspace project via `zerops_env project=true action=set` after provision, so workspace verification doesn't see literal `${STAGE_FRONTEND_URL}` strings. +The platform resolves `${zeropsSubdomainHost}` when injecting the value into services at container start. The frontend consumes `API_URL` via plain `${API_URL}` in `build.envVariables` (baking it into the bundle at compile time) — **no `RUNTIME_` prefix**. The API consumes `FRONTEND_URL` via plain `${FRONTEND_URL}` in `run.envVariables` (for CORS allow-list). The same names must be set on the workspace project via `zerops_env project=true action=set` after provision, so workspace verification doesn't see literal `${FRONTEND_URL}` strings. ## Secret Variables - Defined via GUI, import.yml `envSecrets`, or `dotEnvSecrets` - **Write-only after creation** -- values masked in GUI, cannot be read back via API @@ -10657,7 +10350,7 @@ Env var changes (secret or project) take effect only on container start. The run ## System-Generated Variables Zerops auto-generates variables per service (e.g., `hostname`, `PATH`, DB connection strings). Cannot be deleted. Some read-only (`hostname`), others editable (`PATH`). Can be referenced by other services using `${hostname_varname}`. ## Common Mistakes -- **DO NOT** re-reference auto-injected vars under their own name — self-shadow loop. Applies to BOTH project-level (`STAGE_API_URL: ${STAGE_API_URL}`) AND cross-service (`db_hostname: ${db_hostname}`, `queue_user: ${queue_user}`). +- **DO NOT** re-reference auto-injected vars under their own name — self-shadow loop. Applies to BOTH project-level (`API_URL: ${API_URL}`) AND cross-service (`db_hostname: ${db_hostname}`, `queue_user: ${queue_user}`). - **DO NOT** declare cross-service vars you only want to READ — they are already in the container's OS env. Read via `process.env.db_hostname` / `getenv('db_hostname')` directly. Declare in `run.envVariables` only to RENAME (e.g. `DB_HOST: ${db_hostname}`) or to set mode flags. - **DO NOT** forget restart after GUI/API env changes — process won't see new values - **DO NOT** expect `envReplace` to recurse subdirectories — it does not @@ -10700,113 +10393,6 @@ Contact `support@zerops.io` with Project ID + Organization ID to request changes ---------------------------------------- -# Guides > Local Development - -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` | -| 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"` | -| 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 - ----------------------------------------- - # Guides > Logging Zerops captures stdout/stderr as logs; use syslog output format for severity filtering. Supports forwarding to Better Stack, Papertrail, or self-hosted ELK via syslog. @@ -10876,7 +10462,7 @@ envVariables: ELASTIC_APM_ACTIVE: "true" ELASTIC_APM_SERVICE_NAME: my-app ELASTIC_APM_SERVER_URL: https://apmserver.zerops.app - ELASTIC_APM_SECRET_TOKEN: + ELASTIC_APM_SECRET_TOKEN: ``` ## Prometheus + Grafana Stack Services | Service | Purpose | @@ -11600,7 +11186,7 @@ envVariables: SMTP_PORT: "587" SMTP_USER: apikey envSecrets: - SMTP_PASSWORD: + SMTP_PASSWORD: ``` ## Gotchas 1. **Port 25 is permanently blocked**: Cannot be unblocked — use 587 with STARTTLS @@ -11609,6 +11195,55 @@ envSecrets: ---------------------------------------- +# Guides > Verify Web Agent Protocol + +Sub-agent dispatch protocol for end-to-end verification of a Zerops web +service. The main agent reads `develop-verify-matrix` (atom) for which +services need this protocol; the protocol body itself lives here so it +ships only when fetched, not on every per-turn payload. +Spawn one sub-agent per web-facing target. Substitute `{targetHostname}` +and `{runtime}` with that service's values when constructing the prompt. +--- +## Sub-agent dispatch prompt +``` +Agent(model="sonnet", prompt=""" +Verify Zerops service "{targetHostname}" ({runtime}) works for end users. +## Protocol +1. `zerops_verify serviceHostname="{targetHostname}"` — infrastructure baseline +2. If NOT healthy → VERDICT: FAIL (cite failed checks from zerops_verify response) +3. `zerops_discover service="{targetHostname}"` — get subdomainUrl or connection info +4. Determine reachable URL: + - subdomainUrl available → use it (public HTTPS) + - no subdomain, no custom domain → VERDICT: UNCERTAIN (cannot reach from outside) + - unreachable after timeout → VERDICT: UNCERTAIN +5. `agent-browser open {url}` +6. `agent-browser snapshot` — accessibility tree for AI analysis +7. Evaluate: does the page render meaningful content? + - Interactive elements (buttons, links, forms)? + - Text content (headings, paragraphs)? + - Or empty/broken (empty root div, error page, blank screen)? +8. If concerns: `agent-browser eval "JSON.stringify(Array.from(document.querySelectorAll('script[src]')).map(s=>s.src))"` for loaded scripts +9. For SPAs: `agent-browser eval "window.__errors || []"` AND check if console has errors +## Rules +- zerops_verify unhealthy/degraded → always VERDICT: FAIL (never override infra checks) +- HTTP 401/403 with rendered content (login page, auth challenge) → VERDICT: PASS (auth is working correctly) +- HTTP 401/403 with empty body → VERDICT: UNCERTAIN (cannot determine if intentional) +- zerops_verify healthy + page empty/broken → VERDICT: FAIL (cite what you see) +- zerops_verify healthy + page renders real content → VERDICT: PASS +- agent-browser unavailable or URL unreachable → VERDICT: UNCERTAIN +## Output (mandatory format) +### Infrastructure +zerops_verify status and check summary +### Application +what you observed — DOM content, JS errors, visual state +### Evidence +accessibility tree excerpt or error details +### VERDICT: PASS or FAIL or UNCERTAIN — one-line justification +""") +``` + +---------------------------------------- + # Guides > Vpn Zerops VPN uses WireGuard via `zcli vpn up ` — connects to one project at a time, services accessible by hostname, but env vars are NOT available through VPN. @@ -12232,19 +11867,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a Java service with hostname = "app" and port = 8080 from another service of the same project, simply use `app:8080`. Read more about [how to access a Java service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customizes the Java runtime environment by installing additional dependencies or tools to the runtime base environment. The base Java environment contains {data.alpine.default}, the selected major @@ -12372,25 +12002,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -12412,16 +12036,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Java application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -12464,25 +12084,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -12505,16 +12119,12 @@ Read more about how the [readiness check works](/java/how-to/deploy-process#read #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Java application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -12660,70 +12270,39 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description - hostname - The unique service identifier. - The hostname of the new database will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Java service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -12739,7 +12318,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Java service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -12848,7 +12426,6 @@ Build tools: When you need Maven, Gradle, or other build tools not included by d As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-java-hello-world), a **_recipe_**, containing the most simple Java web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Java running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-java-hello-world/blob/main/import-project/description.yaml)): ```yaml @@ -13131,76 +12708,48 @@ The hostname of the first service will be set to `keydb1`. The highly available The hostname of the second service will be set to `keydb2`. The single container mode will be chosen and the default [auto scaling configuration](/keydb/how-to/scale) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains only KeyDB services but you can create a `description.yaml` with [different types] of services. - Parameter Description - hostname - The unique service identifier. The hostname of the new database will be set to the `hostname` value. Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [KeyDB service types](/references/import-yaml/type-list#database-services) are currently supported. - mode - Defines the operation mode of KeyDB service. HA - Creates a KeyDB cluster with 2 database containers. This mode is suited for production. Zerops always keeps the 2 database containers on different physical machines. All your data is stored redundantly in 2 identical copies. In case of a failure of a container or the underlying physical machine, Zerops automatically disconnects the failed container from the cluster, creates a new container and syncs all data from the remaining copy. Finally the broken container is automatically deleted. NON_HA - Zerops will create a KeyDB database installed in a single container. Useful for non-essential data or dev environments. Your data is stored only in a single container. If the container or the underlying physical machine fails, your data since the last backup are lost. Zerops doesn't provide any automatic repairs of single node KeyDB services. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - :::caution The KeyDB service **hostname** and **mode** are fixed after the service is created. They can't be changed later. ::: @@ -13219,7 +12768,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add KeyDB service to an existing project #### Example Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -13286,7 +12834,6 @@ You can configure scaling settings: - **During import** - Define scaling configuration in your YAML import file using `verticalAutoscaling` parameters - **After service creation** - Navigate to your KeyDB service and select **Automatic scaling configuration** to modify settings ### Basic settings - **CPU Mode**: Choose between shared (cost-effective, variable performance) or dedicated (consistent performance, higher cost). You can change CPU mode once per hour. See [pricing](https://zerops.io/#pricing) for costs. **Resource limits**: Configure minimum and maximum resources for your KeyDB service: - **Lower the maximum** to control costs and prevent over-scaling @@ -13305,7 +12852,6 @@ When a container fails in HA mode, Zerops automatically replaces it with a new c - **Absolute (GB)**: Maintains this amount of free RAM at all times - **Percentage**: Keeps this percentage of total RAM free Consider increasing these values if your database experiences memory-related issues. - :::info Read More For detailed technical parameters and scaling behavior, see [Automatic Scaling and High Availability](/features/scaling#resource-scaling-behavior). ::: @@ -13625,85 +13171,60 @@ The hostname of the first service will be set to `mariadb1`. The highly availabl The hostname of the second service will be set to `mariadb2`. The single container mode will be chosen and the default [auto scaling configuration](/mariadb/how-to/scale) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains only MariaDB services but you can create a `description.yaml` with [different types] of services. - Parameter Description Limitations - hostname A unique service identifier like `mariadb`,`sql`, `db` etc. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type Specifies the service type and version. - See what [MariaDB service types](/references/import-yaml/type-list#database-services) are currently supported. - mode Defines the operation mode of MariaDB service. - HA - Zerops will create a MariaDB cluster with 3 database containers and 2 free database proxies. This mode is suited for production. - Zerops always keep the 3 database containers on different physical machines. All your data is stored redundantly in 3 copies. In case of a failure of a container or the underlying physical machine, Zerops automatically disconnects the failed container from the cluster, creates a new container and syncs all data from the remaining 2 copies. Finally the broken container is automatically deleted. - NON_HA - Zerops will create a MariaDB database installed in a single container. Useful for non-essential data or dev environments. - Your data is stored only in a single container. If the container or the underlying physical machine fails, your data since the last backup are lost. Zerops doesn't provide any automatic repairs of single node MariaDB services. - verticalAutoscaling Defines custom vertical auto scaling parameters - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam Set the minRam or maxRam in GB (float). - - minDisk/maxDisk Set the minDisk or maxDisk in GB (float). - envSecrets Defines [secret environment variables](/features/env-variables#2-secret-variables) for the service - :::caution The MariaDB service **hostname** and **mode** are fixed after the service is created. They can't be changed later. ::: @@ -13722,7 +13243,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add MariaDB service to an existing project #### Example Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -13758,23 +13278,18 @@ Maximum size of the `import.yaml` file is 100 kB. When migrating to MariaDB in Zerops from other database systems, you may need to configure specific system variables to maintain compatibility with your existing applications. #### Setting `lower_case_table_names` for MySQL compatibility The `lower_case_table_names` system variable determines how MariaDB handles table name case sensitivity: - Value Table Name Storage Comparison Behavior - 0 Stored as specified (preserves case) Case-sensitive - 1 Stored in lowercase Case-insensitive - 2 Stored as specified (preserves case) Case-insensitive - :::caution This variable must be configured when the MariaDB instance is first created and cannot be changed afterward. This is a limitation of MariaDB itself, not specific to Zerops. ::: @@ -13948,7 +13463,6 @@ You can configure scaling settings: - **During import** - Define scaling configuration in your YAML import file using `verticalAutoscaling` parameters - **After service creation** - Navigate to your MariaDB service and select **Automatic scaling configuration** to modify settings ### Basic settings - **CPU Mode**: Choose between shared (cost-effective, variable performance) or dedicated (consistent performance, higher cost). You can change CPU mode once per hour. See [pricing](https://zerops.io/#pricing) for costs. **Resource limits**: Configure minimum and maximum resources for your MariaDB service: - **Lower the maximum** to control costs and prevent over-scaling @@ -13967,7 +13481,6 @@ When a container fails in HA mode, Zerops automatically replaces it with a new c - **Absolute (GB)**: Maintains this amount of free RAM at all times - **Percentage**: Keeps this percentage of total RAM free Consider increasing these values if your database experiences memory-related issues. - :::info Read More For detailed technical parameters and scaling behavior, see [Automatic Scaling and High Availability](/features/scaling#resource-scaling-behavior). ::: @@ -14150,15 +13663,12 @@ function SearchComponent() { setResults(data.hits); }; return ( - handleSearch(e.target.value)} placeholder="Search products..." /> - {results.map(hit => ( {hit.name} ))} - ); } ``` @@ -14219,16 +13729,12 @@ You can fine-tune your NATS service by adjusting **environment variables**: :::note If certain variables are not visible in your configuration, they may have been introduced after your service was created. Simply add them as [secret variables](/features/env-variables#2-secret-variables) to access the functionality. ::: - Variable Description - MAX_PAYLOAD Defines the maximum allowed message size for all NATS traffic. Default: 8MB, Maximum: 64MB. See NATS limits documentation for details. - JET_STREAM_ENABLED Controls whether JetStream functionality is enabled. Default: 1 (enabled), Set to 0 to disable. See JetStream Configuration section below for more details. - :::important Configuration changes require a service **restart** to take effect. While NATS itself supports configuration hot-reload, this feature will be implemented in a future Zerops update. ::: @@ -14296,9 +13802,8 @@ For advanced configurations or custom requirements: # Nginx > Faq Question: How do I enable SEO optimization with prerender.io? -Answer: +Answer: Zerops provides built-in prerender.io support. Simply set the `PRERENDER_TOKEN` environment variable with your prerender.io service token. See our [prerender.io documentation](/nginx/how-to/env-variables#prerenderio-support) for details. - ---------------------------------------- @@ -14462,19 +13967,14 @@ For example, to connect to a Nginx static service with hostname = "app" and port Do not use the port **:443**. All the incoming traffic is terminated on the Zerops internal balancer where the SSL certificate is installed and the request is forwarded to your Nginx static service as a **http://** on the port **:80**. ::: Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customizes the Nginx runtime environment by installing additional dependencies or tools to the runtime base environment. The base Nginx environment contains {data.alpine.default}, the selected @@ -14510,7 +14010,6 @@ Some packages or tools can take a long time to install. Therefore, Zerops caches 1. Content of the [build.addToRunPrepare](#copy-folders-or-files-from-your-build-container) and `run.prepareCommands` attributes didn't change from the previous deploy 2. The custom runtime cache wasn't invalidated in the Zerops GUI. To invalidate the Zerops runtime cache go to your service detail in Zerops GUI, choose **Service dashboard & runtime containers** from the left menu and click on the **Open pipeline detail** button. Then click on the **Clear runtime prepare cache** button. - When the prepare cache is used, Zerops doesn't create a prepare runtime container and executes the deployment of your application directly. #### Single or separated shell instances You can configure your prepare commands to be run in a single shell instance or multiple shell instances. @@ -14614,25 +14113,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -14655,16 +14148,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Nginx application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -14692,25 +14181,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -14733,16 +14216,12 @@ Read more about how the [readiness check works](/nginx/how-to/deploy-process#rea #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Nginx application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -14874,84 +14353,51 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" ``` #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains an Nginx static service but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [nginx service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -14967,7 +14413,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Nginx static service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -15029,7 +14474,6 @@ The default Nginx Static runtime environment contains: If your Nginx Static service needs additional tools beyond the default environment (SSL management, monitoring, security tools, etc.), you'll need to build a custom runtime image. Here are Nginx Static-specific examples of configuring custom runtime images in your `zerops.yml`: ### Basic Nginx Static Setup - For complete configuration details, see the [runtime prepare phase configuration guide](/features/pipeline#configuration). ## Process and Caching ### How Runtime Prepare Works @@ -15126,22 +14570,18 @@ The Nginx configuration will automatically handle the rest—no additional confi ## Prerender.io Support Zerops provides built-in prerender.io support for SEO optimization. Configure it using these environment variables: - Variable Required Description Default - PRERENDER_TOKEN Yes Your prerender.io service token - - PRERENDER_HOST No Prerender service host service.prerender.io - :::tip Set `PRERENDER_TOKEN` as a secret environment variable in Zerops GUI for security. ::: @@ -15209,13 +14649,12 @@ Have you build something that others might find useful? Don't hesitate to share # Nodejs > Faq Question: Why is my Node.js build timing out or hanging on interactive prompts? -Answer: +Answer: If your build process seems to be hanging or timing out, check your logs for interactive prompts that are waiting for input, such as: ``` ? The modules directory at "/build/source/node_modules" will be removed and reinstalled from scratch. Proceed? (Y/n) ‣ true ``` Set the environment variable `CI: true` to resolve the problem. This allows the installation to proceed automatically without requiring manual confirmation. - ---------------------------------------- @@ -15596,19 +15035,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a Node.js service with hostname = "app" and port = 3000 from another service of the same project, simply use `app:3000`. Read more about [how to access a Node.js service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the Node.js runtime environment by installing additional dependencies or tools to the runtime base environment. The base Node.js environment contains {data.alpine.current} the selected @@ -15736,25 +15170,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -15776,16 +15204,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Node.js application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -15828,25 +15252,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -15869,16 +15287,12 @@ Read more about how the [readiness check works](/nodejs/how-to/deploy-process#re #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Node.js application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -16024,86 +15438,50 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Node.js and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - The hostname of the new database will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Node.js service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -16119,7 +15497,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Node.js service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -16227,7 +15604,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-nodejs), a **_recipe_**, containing the most simple Node.js web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Node.js app running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-nodejs/blob/main/zerops-project-import.yaml)): ```yaml @@ -16284,24 +15660,17 @@ The bucket quota size was defined when the Object storage service was created. Y ## Copy access details from Zerops GUI You will find the Object storage access details under the **Access details** button in the project dashboard page. The same information is available in the service detail page under the **Access & bucket details** menu. - ### Object storage access parameters - Parameter Description - API URL URL of the Object storage service - Bucket Name Bucket is created with a name based on the selected service name and a random prefix. The name of the bucket is fixed and cannot be changed later. - Access Key ID The S3 Access Key ID - Secret Key The S3 Secret Key - ## Use Object storage environment variables Zerops creates default environment variables for each Object storage service to help you with connection from any runtime services in the same project. To avoid the need to copy Object storage access parameters manually, use environment variables in your runtime service. ### Prefix the environment variable key @@ -16310,36 +15679,25 @@ All services of the same project can reference environment variables from other To access the `bucketName` env variable of the `upload` service, use `upload_bucketName` as the env variable key. To access the `secretAccessKey` env variable of the `storage` service, use `storage_secretAccessKey` as the env variable key. ### Object storage environment variables List of service environment variables is available in Zerops GUI. Go to an Object storage service detail and choose **Environment variables** in the left menu. - Zerops creates following environment variables when the Object storage service is created: - Variable Description - apiUrl URL of the Object storage service - accessKeyId The S3 Access Key ID - secretAccessKey The S3 Secret Key - bucketName Bucket is created with a name based on the selected service name and a random prefix. The name of the bucket is fixed and cannot be changed later. - quotaGBytes The bucket quota in GB. - projectId ID of the project. Generated by Zerops. - serviceId ID of the Object storage service. Generated by Zerops. - hostname The name of the Object storage service. - ---------------------------------------- @@ -16371,41 +15729,28 @@ Each Object storage service can only contain one bucket. If your application nee Bucket will be created with a name based on the given service name and a random prefix. The name of the bucket cannot be changed later. #### Access policy Select one of the basic policy templates: - Template Description - Public read - Allows anyone: - to read the bucket's location `(s3:GetBucketLocation)` to list all bucket's objects `(s3:ListBucket)` to get any object of the bucket `(s3:GetObject)` - Public objects read - Allows anyone: - to read the bucket's location `(s3:GetBucketLocation)` to get any object of the bucket `(s3:GetObject)` - Public read write - Allows anyone: - to read the bucket's location `(s3:GetBucketLocation)` to list all bucket's objects `(s3:ListBucket)` to get any object of the bucket `(s3:GetObject)` to create a new object in the bucket `(s3:PutObject,s3:ListMultipartUploadParts, s3:AbortMultipartUpload, s3:ListBucketMultipartUploads)` to delete any object of the bucket `(s3:DeleteObject)` - Public write Allows anyone to create objects in the bucket `(PutObject action)` - Private Denies the access to unauthenticated users. - Or you can set your own access policy in the [IAM Policy JSON format](https://min.io/docs/minio/linux/administration/identity-access-management/policy-based-access-control.html#policy-document-structure). #### Example: ```yaml @@ -16434,7 +15779,6 @@ The `{{ .BucketName }}` variable will be replaced by the bucket name. The bucket's policy can be changed later in Zerops GUI. #### Quota Set the bucket quota size in GB. The quota must be set manually. It can be changed later in Zerops GUI. Zerops doesn't support Object storage autoscaling. You can set the bucket quota from 1 to 100 GB. - ## Create Object storage using zCLI zCLI is the Zerops command-line tool. To create a new Object storage service via the command-line, follow these steps: 1. [Install & setup zCLI](/references/cli) @@ -16471,76 +15815,47 @@ services: The yaml file describes your future project infrastructure. The project will contain one Object storage service named `upload`. The bucket quota will be set to 73 GB and the bucket access policy will be set to `public-write`. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. Maximum 255 characters - description Optional. Description of the new project. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains only Object storage service but you can create a description.yaml with different types of services. - Parameter Description - hostname - The unique service identifier. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the Object storage type objectstorage and version. - Set type: `objectstorage` - Limitations: - Currently `objectstorage` or `object-storage` is available. - objectStorageSize - The size of the bucket quota in GB. - Limitations: - Set a whole number between 1 and 100. - objectStoragePolicy - Optional. Either `objectStoragePolicy` or `objectStorageRawPolicy` is required. - Set one of allowed values: - `public-read` `public-objects-read` `public-read-write` `public-write` `private` - Read more about the basic policy templates. - objectStorageRawPolicy - Optional. Either `objectStoragePolicy` or `objectStorageRawPolicy` is required. - Set your own access policy in the IAM Policy JSON format. - The `{{ .BucketName }}` variable will be replaced by the bucket name. - Zerops creates one bucket automatically for each new Object storage service. :::note Each Object storage service can only contain one bucket. If your application needs multiple buckets, add more Object storage services. @@ -16561,7 +15876,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Object service to an existing project Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: ```yaml @@ -16664,7 +15978,6 @@ Zerops creates one bucket automatically for each new Object storage service. Each Object storage service can only contain one bucket. If your application needs multiple buckets, add more Object storage services. ::: To change the bucket size or the access policy, go to the **Access & bucket details** in the Object service detail in Zerops GUI, scroll down and click on the **Configure bucket quota size and access policy** button. - ### Quota Set the bucket quota size in GB. The quota must be set manually. It can be changed later in Zerops GUI. Zerops doesn't support Object storage autoscaling. You can set the bucket quota from 1 to 100 GB. ### Access policy @@ -17105,19 +16418,14 @@ For example, to connect to a PHP service with hostname = "app" and port = 80 fro Do not use the port **:443**. All the incoming traffic is terminated on the Zerops internal balancer where the SSL certificate is installed and the request is forwarded to your PHP+Nginx / PHP+Apache service as a **http://** on the port **:80**. ::: Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the PHP runtime environment by installing additional dependencies or tools to the runtime base environment. The base PHP environment contains {data.alpine.default}, the selected @@ -17153,7 +16461,6 @@ Some packages or tools can take a long time to install. Therefore, Zerops caches 1. Content of the [build.addToRunPrepare](#copy-folders-or-files-from-your-build-container) and `run.prepareCommands` attributes didn't change from the previous deploy 2. The custom runtime cache wasn't invalidated in the Zerops GUI. To invalidate the Zerops runtime cache go to your service detail in Zerops GUI, choose **Service dashboard & runtime containers** from the left menu and click on the **Open pipeline detail** button. Then click on the **Clear runtime prepare cache** button. - When the prepare cache is used, Zerops doesn't create a prepare runtime container and executes the deployment of your application directly. #### Single or separated shell instances You can configure your prepare commands to be run in a single shell instance or multiple shell instances. The format is identical to [build commands](#buildcommands). @@ -17258,25 +16565,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -17299,16 +16600,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your PHP application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -17352,25 +16649,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -17393,16 +16684,12 @@ Read more about how the [readiness check works](/php/how-to/deploy-process#readi #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your PHP application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -17548,86 +16835,50 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains PHP and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - The hostname of the new service will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [PHP service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -17643,7 +16894,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add PHP service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -17849,14 +17099,11 @@ The default PHP+Apache service has following Apache configuration: ServerName localhost DocumentRoot {{.DocumentRoot}} DirectoryIndex index.htm index.html index.shtml index.php index.phtml - Options -Indexes Options FollowSymLinks AllowOverride All Require all granted - SetHandler "proxy:unix:{{.PhpSocket}}|fcgi://localhost/" - ErrorLog "| /usr/bin/logger -tapache -plocal1.err" CustomLog "| /usr/bin/logger -tapache -plocal1.notice" combined ``` @@ -17964,7 +17211,6 @@ zerops: As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-php-hello-world), a **_recipe_**, containing the most simple PHP web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of PHP running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-php-hello-world/blob/main/import-project/description.yaml)): ```yaml @@ -18002,26 +17248,22 @@ Have you build something that others might find useful? Don't hesitate to share # Postgresql > Faq Question: How do I properly use PostgreSQL in HA mode? -Answer: +Answer: In High Availability (HA) mode, PostgreSQL runs on multiple containers with a primary node and replicas. To get the most out of this setup: - Use port `5432` for all write operations (INSERT, UPDATE, DELETE) — this always routes to the primary node - Use port `5433` for read operations (SELECT) — this distributes queries across all replicas, improving performance The read replica port (`5433`) is only available in HA mode. If you're running PostgreSQL in single container (NON_HA) mode, only port `5432` is available. See [Connection Parameters](/postgresql/how-to/connect#connection-parameters) for all available ports and environment variables. - Question: Why is my connection to PostgreSQL from third-party software failing? -Answer: - *One possible cause:* +Answer: + *One possible cause:* The connection string in Zerops always starts with `postgresql://`. While the official PostgreSQL documentation states that both `postgresql://` and `postgres://` URIs are valid, some software requires the shorter `postgres://` version. - To resolve this, create your own environment variable with the correct URI. For example, if your PostgreSQL service is named `db`, use the following format: - ``` postgres://${db_user}:${db_password}@${db_hostname}:${db_port} ``` - ---------------------------------------- @@ -18075,52 +17317,42 @@ Zerops provides several ways to connect to PostgreSQL: You'll find PostgreSQL connection details in the service detail page under the **Peek access details** button (shows hostname, port, user, password, and connection string). The full list of connection-related environment variables is available in the service detail under **Environment variables**. ### Connection Parameters - Parameter Internal External (TLS) Env Variable - Hostname Service hostname Public IP address `hostname` - Port (primary) 5432 6432 (via pgBouncer) `port` / `portTls` - Port (replicas, HA only) 5433 N/A `portReplicas` - User Identical to hostname Same as internal `user` - Password Generated at creation Same as internal `password` - Connection string (primary) `postgresql://${user}:${password}@${hostname}:5432/${dbName}` Same format with TLS port `connectionString` / `connectionTlsString` - Connection string (replicas, HA only) `postgresql://${user}:${password}@${hostname}:5433/${dbName}` N/A `connectionStringReplicas` - Database name db Same as internal `dbName` - :::tip If you're running PostgreSQL in High Availability (HA) mode, configure your application to route read queries to port **5433**. This distributes the load across all replicas, reducing pressure on the primary node and improving overall throughput. ::: @@ -18286,97 +17518,61 @@ The hostname of the first service will be set to `postgresql1`. The [high availa The hostname of the second service will be set to `postgresql2`. The [single container](/features/scaling#single-container-mode) mode will be chosen and the default [auto scaling configuration](/postgresql/how-to/scale) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in the `services:` section is required. You can create a project with multiple services. The example above contains only PostgreSQL services but you can create a `description.yaml` with [different types] of services. - Parameter Description - hostname - The unique service identifier. - - The hostname of the new database will be set to the `hostname` value. - + The hostname of the new database will be set to the `hostname` value. Limitations: - - duplicate services with the same name in the same project are forbidden - - maximum 25 characters - - must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [PostgreSQL service types](/references/import-yaml/type-list#database-services) are currently supported. - mode - Defines the operation mode of the PostgreSQL service. - HA - Creates a PostgreSQL cluster with 3 database containers and 2 free database proxies. This mode is suited for production. - Zerops always keeps the 3 database containers on different physical machines. All your data is stored redundantly in 3 identical copies. In case of a failure of a container or the underlying physical machine, Zerops automatically disconnects the failed container from the cluster, creates a new container and syncs all data from the remaining 2 copies. Finally, the broken container is automatically deleted. - In HA mode, a dedicated read replica port (5433) is available, allowing you to route read queries to replicas for better performance. See Connection Parameters for details. - NON_HA - Zerops will create a PostgreSQL database installed in a single container. Useful for non-essential data or dev environments. - Your data is stored only in a single container. If the container or the the underlying physical machine fails, your data since the last backup are lost. Zerops doesn't provide any automatic repairs of a single node PostgreSQL services. - verticalAutoscaling - Optional. Defines custom vertical auto-scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - :::caution The PostgreSQL service **hostname** and **mode** are fixed after the service is created. They can't be changed later. ::: @@ -18395,7 +17591,6 @@ Zerops will create a project and one or more services based on the `description. The maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add PostgreSQL service to an existing project #### Example Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -18590,7 +17785,6 @@ You can configure scaling settings: - **During import** - Define scaling configuration in your YAML import file using `verticalAutoscaling` parameters - **After service creation** - Navigate to your PostgreSQL service and select **Automatic scaling configuration** to modify settings ### Basic settings - **CPU Mode**: Choose between shared (cost-effective, variable performance) or dedicated (consistent performance, higher cost). You can change CPU mode once per hour. See [pricing](https://zerops.io/#pricing) for costs. **Resource limits**: Configure minimum and maximum resources for your PostgreSQL service: - **Lower the maximum** to control costs and prevent over-scaling @@ -18612,7 +17806,6 @@ When a container fails in HA mode, Zerops automatically replaces it with a new c - **Absolute (GB)**: Maintains this amount of free RAM at all times - **Percentage**: Keeps this percentage of total RAM free Consider increasing these values if your database experiences memory-related issues. - :::info Read More For detailed technical parameters and scaling behavior, see [Automatic Scaling and High Availability](/features/scaling#resource-scaling-behavior). ::: @@ -19000,19 +18193,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a Python service with hostname = "app" and port = 8000 from another service of the same project, simply use `app:8000`. Read more about [how to access a Python service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the Python runtime environment by installing additional dependencies or tools to the runtime base environment. The base Python environment contains {data.alpine.default}, the selected @@ -19138,25 +18326,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -19178,16 +18360,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Python application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -19230,25 +18408,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -19271,16 +18443,12 @@ Read more about how the [readiness check works](/python/how-to/deploy-process#re #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Python application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -19426,86 +18594,50 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Python and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - The hostname of the new service will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Python service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -19521,7 +18653,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Python service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -19629,7 +18760,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-python-hello-world), a **_recipe_**, containing the most simple Python web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Python running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-python-hello-world/blob/main/import-project/description.yaml)): ```yaml @@ -20079,19 +19209,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a Rust service with hostname = "app" and port = 8080 from another service of the same project, simply use `app:8080`. Read more about [how to access a Rust service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the Rust runtime environment by installing additional dependencies or tools to the runtime base environment. The base Rust environment contains {data.alpine.default}, the selected @@ -20127,7 +19252,6 @@ Some packages or tools can take a long time to install. Therefore, Zerops caches 1. Content of the [build.addToRunPrepare](#copy-folders-or-files-from-your-build-container) and `run.prepareCommands` attributes didn't change from the previous deploy 2. The custom runtime cache wasn't invalidated in the Zerops GUI. To invalidate the Zerops runtime cache go to your service detail in Zerops GUI, choose **Service dashboard & runtime containers** from the left menu and click on the **Open pipeline detail** button. Then click on the **Clear runtime prepare cache** button. - When the prepare cache is used, Zerops doesn't create a prepare runtime container and executes the deployment of your application directly. #### Single or separated shell instances You can configure your prepare commands to be run in a single shell instance or multiple shell instances. The format is identical to [build commands](#buildcommands). @@ -20219,25 +19343,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -20259,16 +19377,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Rust application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -20311,25 +19425,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -20352,16 +19460,12 @@ Read more about how the [readiness check works](/rust/how-to/deploy-process#read #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Rust application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -20507,84 +19611,51 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description Limitations - name The name of the new project. Duplicates are allowed. - description Optional. Description of the new project. Maximum 255 characters. - tags Optional. One or more string tags. Tags do not have a functional meaning, they only provide better orientation in projects. - At least one service in `services:` section is required. You can create a project with multiple services. The example above contains Rust and PostgreSQL services but you can create a `description.yaml` with your own combination of [services](/features/infrastructure). - Parameter Description - hostname - The unique service identifier. - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Rust service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -20600,7 +19671,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Rust service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -20708,7 +19778,6 @@ System packages for processing: When your app processes images, videos, or files As said, there is no need for coding yet, we have created a [Github repository ↗](https://github.com/zeropsio/recipe-rust-hello-world), a **_recipe_**, containing the most simple Rust web application. The repo will be used as a source from which the app will be built. This is the most bare-bones example of Rust running in Zerops — as few libraries as possible, just a simple endpoint with connect, read and write to a Zerops PostgreSQL database. - 1. Log in/sign up to [Zerops GUI ↗](https://app.zerops.io) 2. In the **Projects** box click on **Import a project** and paste in the following YAML config ([source ↗](https://github.com/zeropsio/recipe-rust-hello-world/blob/main/import-project/description.yaml)): ```yaml @@ -20777,7 +19846,6 @@ This page covers how to connect an existing shared storage to runtime services a When creating a new shared storage service, you can directly select which runtime services it should be connected to. See [Create Shared Storage](/shared-storage/how-to/create) for details about the creation process. ### Connect an existing shared storage For existing storage, go to the shared storage service detail page and select **Shared storage connections**. Toggle ON any runtime services you wish to connect to this storage. - ## Disconnect a shared storage in Zerops GUI To disconnect storage, access the shared storage service detail page, select **Shared storage connections**, and toggle OFF the desired runtime service. @@ -20798,7 +19866,6 @@ The hostname is fixed after the service is created. It can't be changed later. ::: ### Connect to Services Select one or more project's runtime services in the Share with Services block: - The new Shared Storage will be connected to the selected runtimes. :::note Runtime services can be connected and disconnected at any time even after the shared storage is created. @@ -20811,7 +19878,6 @@ See [Technical Details](/shared-storage/tech-details#deployment-modes) for more ::: ### Set Auto Scaling Configuration Configure vertical auto scaling parameters to control resource allocation and costs. - :::note For detailed information about auto scaling capabilities and recommendations, see [Technical Details](/shared-storage/tech-details#auto-scaling-configuration). ::: @@ -20871,7 +19937,6 @@ Several options are available to help you monitor your Shared Storage: Once a Shared Storage is [connected](/shared-storage/how-to/connect) to a runtime service, Zerops will create a new folder `/mnt/[shared storage name]` in the runtime service's filesystem. For example, `/mnt/teststorage` for a `teststorage` Shared Storage: - :::note The content of this folder is shared among all containers of the connected runtime service. If you connect multiple runtimes, the content of the folder will be shared among all containers of these services. @@ -20983,7 +20048,6 @@ You can change the auto scaling parameters later. The Static service provides a way to serve static content through a pre-configured Nginx setup. It balances simplicity with the flexibility needed for modern web applications. Deploy an Analog app with static hosting in seconds. All you need is a Zerops account. - ## Quick Start Add a Static service to your project by including this in your `zerops.yaml`: ```yaml title="zerops.yaml" @@ -21587,7 +20651,7 @@ zerops: base: ubuntu@24.04 # OPTIONAL. Build your application buildCommands: - - + - ... ``` Build commands are optional. Zerops triggers each command in the defined order in a dedicated build container, running from the `/build/source` directory. @@ -21748,19 +20812,14 @@ _OPTIONAL._ Specifies one or more internal ports on which your application will Projects in Zerops represent a group of one or more services. Services can be of different types (runtime services, databases, message brokers, object storage, etc.). All services of the same project share a **dedicated private network**. To connect to a service within the same project, just use the service hostname and its internal port. For example, to connect to a Ubuntu service with hostname = "app" and port = 8080 from another service of the same project, simply use `app:8080`. Read more about [how to access a Ubuntu service](/references/networking/internal-access#basic-service-communication). Each port has following attributes: - Parameter Description - port Defines the port number. You can set any port number between 10 and 65435. Ports outside this interval are reserved for internal Zerops systems. - protocol Optional. Defines the protocol. Allowed values are TCP or UDP. Default value is TCP. - httpSupport Optional. httpSupport = true is the default setting for TCP protocol. Set httpSupport = false if a web server isn't running on the port. Zerops uses this information for the configuration of public access. httpSupport = true is available only in combination with the TCP protocol. - ### prepareCommands _OPTIONAL._ Customises the Ubuntu runtime environment by installing additional dependencies or tools to the runtime base environment. The base Ubuntu environment contains {data.ubuntu.default}, Zerops command line tool and `git` and `wget`. To install additional packages or tools add one or more prepare commands: @@ -21884,25 +20943,19 @@ _OPTIONAL._ Defines a health check. #### httpGet Configures the health check to request a local URL using a HTTP GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -21924,16 +20977,12 @@ zerops: #### exec Configures the health check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Ubuntu application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -21976,25 +21025,19 @@ _OPTIONAL._ Defines a readiness check. Read more about how the [readiness check #### httpGet Configures the readiness check to request a local URL using a http GET method. Following attributes are available: - Parameter Description - port Defines the port of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - path Defines the URL path of the HTTP GET request. The readiness check will trigger a GET request on {'http://127.0.0.1:{port}/{path}'} - host Optional. The readiness check is triggered from inside of your runtime container so it always uses the localhost 127.0.0.1. If you need to add a host to the request header, specify it in the host attribute. - scheme Optional. The readiness check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: https - **Example:** ```yaml zerops: @@ -22017,16 +21060,12 @@ Read more about how the [readiness check works](/ubuntu/how-to/deploy-process#re #### exec Configures the readiness check to run a local command. Following attributes are available: - Parameter Description - command - Defines a local command to be run. The command has access to the same environment variables as your Ubuntu application. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example below. - **Example:** ```yaml zerops: @@ -22051,7 +21090,6 @@ Read more about how the [readiness check works](/ubuntu/how-to/deploy-process#re # Ubuntu > How To > Build Process - ## Build process overview Zerops starts a temporary build container and performs the following actions: 1. **Installs the build environment** - Sets up base system and runtime @@ -22068,7 +21106,7 @@ Configure your build process in your `zerops.yaml` file according to the full b The default build environment contains: - {data.ubuntu.default} - [zCLI](/references/cli), Zerops command line tool -- +- ### Customize build environment To install additional packages or tools, add one or more build.prepareCommands to your `zerops.yaml`. :::info @@ -22076,23 +21114,18 @@ The application code is available in the `/build/source` folder in your build co ::: ### Build hardware resources All runtime services use the same hardware resources for build containers: - HW resource Minimum Maximum - CPU cores 1 5 - RAM 8 GB 8 GB - Disk 1 GB 100 GB - Build containers start with minimum resources and scale vertically up to maximum capacity as needed. ### Build time limit The time limit for the whole build pipeline is **1 hour**. After 1 hour, Zerops will terminate the build pipeline and delete the build container. @@ -22239,70 +21272,39 @@ S3_ACCESS_SECRET="ogFthuiLYki8XoL73opSCQ" The hostname of the PostgreSQL service will be set to "db". The [single container](/features/scaling#single-container-mode)(/features/scaling#deployment-modes-databases-and-shared-storage) mode will be chosen and the default auto [scaling configuration](/postgresql/how-to/scale#configure-scaling) will be set. #### Description of description.yaml parameters The `project:` section is required. Only one project can be defined. - Parameter Description - hostname - The unique service identifier. - The hostname of the new database will be set to the `hostname` value. - Limitations: - duplicate services with the same name in the same project are forbidden maximum 25 characters must contain only lowercase ASCII letters (a-z) or numbers (0-9) - type - Specifies the service type and version. - See what [Ubuntu service types](/references/import-yaml/type-list#runtime-services) are currently supported. - verticalAutoscaling - Optional. Defines custom vertical auto scaling parameters. - All verticalAutoscaling attributes are optional. Not specified attributes will be set to their default values. - - cpuMode - Optional. Accepts `SHARED`, `DEDICATED` values. Default is `SHARED` - - minCpu/maxCpu - Optional. Set the minCpu or maxCpu in CPU cores (integer). - - minRam/maxRam - Optional. Set the minRam or maxRam in GB (float). - - minDisk/maxDisk - Optional. Set the minDisk or maxDisk in GB (float). - minContainers - Optional. Default = 1. Defines the minimum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - maxContainers - Defines the maximum number of containers for horizontal autoscaling. - Limitations: - Current maximum value = 10. - envSecrets - Optional. Defines one or more secret env variables as a key value map. See env variable restrictions. - ### Create a project based on the description.yaml When you have your `description.yaml` ready, use the `zcli project project-import` command to create a new project and the service infrastructure. ```sh @@ -22318,7 +21320,6 @@ Zerops will create a project and one or more services based on the `description. Maximum size of the `description.yaml` file is 100 kB. You don't specify the project name in the `zcli project project-import` command, because the project name is defined in the `description.yaml`. If you have access to more than one client, you must specify the client ID for which the project is to be created. The `clientID` is located in the Zerops GUI under the client name on the project dashboard page. - ### Add Ubuntu service to an existing project #### Example: Create a directory `my-project` if it doesn't exist. Create an `import.yaml` file inside the `my-project` directory with following content: @@ -22379,7 +21380,7 @@ It is also a great option when you need a specific version of a technology (like The default runtime environment contains: - {data.ubuntu.default} - [zCLI](/references/cli) -- +- ### When You Need a Custom Runtime Image Since Ubuntu serves as a general-purpose base, you'll likely want to customize it for your specific use case. Common scenarios include: :::important @@ -22513,6 +21514,1014 @@ For advanced configurations or custom requirements: ---------------------------------------- +# Zcp > Concept > How It Works + +ZCP turns product or app intent into a verified deployed app by giving the coding agent two things it normally lacks: current knowledge of the Zerops project and the tools to change, deploy, inspect, and verify it. +## The model in one pass +Product intent enters a project loop: read current state, decide whether the needed services already exist, change the app and its Zerops wiring, deploy, verify from evidence, and return either proof or a concrete blocker. +```mermaid +flowchart TD + intent["Product intent +Build a task board where tasks stay saved after refresh."] + context["ZCP context +live services, env vars, logs, events, +runtime guidance, available operations"] + target["Project fit +use existing services or create missing ones; +select the app runtime, +not the zcp setup service"] + app["Build the app +code, zerops.yaml, env wiring"] + deploy["Deploy through Zerops"] + verify{"Verify real behavior +endpoint or UI"} + fix["Read evidence +logs, events, checks"] + done["Done +URL, endpoint, or UI proof"] + blocker["Blocker +missing credential, decision, +or repeated failure"] + delivery["Future delivery +direct deploy, git push, or CI handoff"] + intent --> context --> target + target --> app --> deploy --> verify + verify -->|fixable| fix --> app + verify -->|verified| done --> delivery + verify -->|needs human| blocker + classDef user fill:#f7fbff,stroke:#2d72d9,stroke-width:1.5px,color:#172033; + classDef zcpbox fill:#eef7f1,stroke:#32845a,stroke-width:1.5px,color:#172033; + classDef work fill:#fffaf0,stroke:#c47f17,stroke-width:1.5px,color:#172033; + classDef done fill:#f5f3ff,stroke:#7157d9,stroke-width:1.5px,color:#172033; + classDef stop fill:#fff1f2,stroke:#d33f49,stroke-width:1.5px,color:#172033; + class intent user; + class context zcpbox; + class target,app,deploy,verify,fix work; + class done,delivery done; + class blocker stop; +``` +The important signals are simple: the agent read current project state, named the target runtime, verified the requested behavior, and explained what remains. +## Where ZCP runs +ZCP can run in two places: +| Path | What runs where | What changes for you | +|---|---|---| +| Remote setup | A `zcp@1` service runs the `zcp` binary inside the Zerops project. **Include Coding Agent** adds the bundled agent CLI and preconfigures it to use ZCP; **Cloud IDE** adds browser VS Code. | The work stays inside the project. The agent can use project-private networking and SSHFS mounts for runtime files. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your editor's agent talks to it. | Your checkout and dev server stay local. Managed services are reached over VPN, and `.env` generation bridges project credentials into your local app. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your editor's agent talks to it. | Your checkout and dev server stay local. Managed services are reached over VPN, and `.env` generation bridges project credentials into your local app. | +The project-scoped control plane is the same idea in both paths. The filesystem, network path, deploy source, and safety profile are different. See [Choose remote or local setup](/zcp/setup/choose-workspace). +## What ZCP reads +ZCP reads live project state instead of relying on a long prompt: +- services and whether they are runtime or managed dependencies, +- runtime layout, such as one app runtime, a dev+stage pair, or a local checkout linked to a Zerops runtime, +- service env-var keys and references, +- build/deploy events, runtime logs, and verification results, +- the current work state when a session is interrupted. +Recovery starts from live state. If a session gets confused or interrupted, ask the agent to read ZCP status before changing anything else. +## What counts as done +A task is not done when code is written, or when a build succeeds. A ZCP task is done when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the URL or a concrete blocker. +Workflow path: [Build with ZCP](/zcp/workflows/build-with-zcp). Exact terms and runtime layouts: [Workflow terms](/zcp/reference/agent-workflow). + +---------------------------------------- + +# Zcp > Glossary + +## Core names +**ZCP** - Zerops Control Plane for coding agents: the project-scoped control plane documented in this section. +**ZCP operations** - the project-scoped operations surface exposed to a coding agent. In MCP clients, this appears as the ZCP MCP server. +**`zcp` binary** - the executable. Runs inside remote setup or on your laptop in local setup. +**zCLI** - the Zerops command-line client for humans, scripts, VPN, and CI. It coexists with ZCP. +**zsc** - the in-container Zerops Setup Control utility used from `zerops.yaml`. +## Where ZCP runs +**Remote setup** - ZCP running inside a Zerops `zcp@1` service in the project. +**`zcp@1` service** - the Zerops service type behind remote setup. +**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI and preconfigures it to use the project's ZCP operations. +**Cloud IDE** - browser-based VS Code served by remote setup. +**AI Agent environment** - a recipe environment preset that creates app services plus remote setup with **Include Coding Agent** enabled. It is a recipe preset, not a separate ZCP setup path. +**Local setup** - the `zcp` binary running on your laptop after `zcp init`, while your editor or CLI agent talks to it. +**Local agent bridge** - local setup with the `zcp` binary connected to a local agent client. +## Workflows and results +**Service setup** - the infrastructure phase behind an app prompt. It uses existing runtime and managed services when they fit, creates missing services when needed, then stops before app code, `zerops.yaml`, or deploy. +**Deploy, verify, and fix** - the app phase. It scopes the runtime, changes code/config, deploys, verifies reachability, verifies requested behavior, and fixes failures. +**Runtime scope** - the app runtime the change targets, such as `appdev`, `appstage`, `app`, or a linked local target. +**Delivery choice** - what happens to future changes after a verified deploy: direct deploy, git-push, or external handoff. +**ZCP status** - a live project read the agent can use after interruption or confusion. Ask for it before letting the agent change anything else. +**Blocker** - a clear stop state with evidence: failure category, runtime in scope, what was tried, and what decision or credential is needed. +## Project and runtime terms +**Runtime service** - a service that runs app code. +**Managed service** - database, cache, queue, search, storage, or similar dependency. It provides connection details; it is not an app deploy target. +**Runtime layout** - which app runtime services ZCP should use: +- `standard` - dev runtime plus explicit stage runtime. +- `dev` - one mutable development runtime. +- `simple` - one runtime with no dev/stage split. +- `local-stage` - local checkout linked to one Zerops runtime as deploy target. +- `local-only` - local checkout with no linked runtime yet. +**Service scaling mode** - Zerops scaling setting such as `HA` or `NON_HA`. Different from runtime layout. +**Stage** - review or promotion target. Stage is not production. +## Credentials +**`ZCP_API_KEY`** - project-scoped Zerops token used by ZCP. +**`GIT_TOKEN`** - git provider credential used by remote git-push delivery when needed. +**`ZEROPS_TOKEN`** - Zerops token commonly used by GitHub Actions or external CI that runs `zcli`. + +---------------------------------------- + +# Zcp > Overview + +ZCP is the Zerops control plane setup that lets a coding agent work from real project state instead of a hand-written operations checklist. It gives the agent current project knowledge, guarded project operations, deploy/verify evidence, and Zerops guidance for connecting an app to its infrastructure. +It can run remotely inside a `zcp@1` service with **Include Coding Agent** enabled, or locally as the `zcp` binary initialized beside your CLI/editor agent. In both cases, the developer stays focused on product intent, technology choices, acceptance criteria, and decisions that require human judgment. +## What ZCP does +ZCP makes four things available to the agent inside a normal Zerops project: +**Project awareness.** The agent can read services, runtime layout, env vars, logs, events, and current work state instead of asking you to describe the project. +**Infrastructure wiring.** The agent can separate the `zcp` setup service, app runtime services, and managed dependencies, then connect the app to the right env vars and service references. +**Deploy and verify loop.** The agent can prove the app works on the selected runtime, not stop after writing code or seeing a green build. +**Delivery choice.** After verified work, the agent can keep deploying directly, push to git, or use external handoff to your CI or a human. +You provide product intent and judgment: what to build, which runtime or stage matters when you care, and what result counts as done. +## A good ZCP session +A ZCP-backed session should be product-led and evidence-based: +- The agent reads the project before changing it. +- It uses existing services when they fit and creates missing infrastructure when they do not. +- It deploys and verifies the requested behavior on the real endpoint or UI. +- It ends with a URL or proof of behavior, or with a blocker that names the missing decision, credential, or repeated failure. +## What you no longer have to do +ZCP is valuable because it removes the operational checklist from the prompt. You should not have to: +- describe every service already in the project, +- decide which env-var references connect the app to managed services, +- copy database credentials, logs, or deploy timelines into chat, +- repeat deploy, verify, log-reading, and URL-reporting instructions on every app task, +- translate a failed build or broken route into a guess before the agent can act, +- choose internal workflow names before describing what you want built. +You still own the product intent, technology constraints, acceptance criteria, credentials that live outside Zerops, and approval for destructive actions. +## Where to start +| Goal | Page | +|---|---| +| Try ZCP once with no local install | [Quickstart](/zcp/quickstart) | +| Understand the model before setup | [How ZCP works](/zcp/concept/how-it-works) | +| Choose remote vs local | [Choose remote or local setup](/zcp/setup/choose-workspace) | +| Add remote setup | [Set up remote ZCP](/zcp/setup/hosted-workspace) | +| Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | +| Build or change an app | [Build with ZCP](/zcp/workflows/build-with-zcp) | +| Decide how finished work ships | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | +| Understand tokens and boundaries | [Trust model](/zcp/security/trust-model) | +| Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | +| Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | +| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | +| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | +## What stays outside ZCP +ZCP is a control plane for agents, not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, scaling, and service references remain canonical for platform behavior. +## Names that matter +- **ZCP** - Zerops Control Plane in its coding-agent setup. +- **Remote setup** - a `zcp@1` service inside the Zerops project. The service runs the `zcp` binary and exposes ZCP to the agent from inside the project. +- **Local setup** - the `zcp` binary installed on your machine and initialized in a project folder with `zcp init`. +- **Include Coding Agent** - the remote setup option that adds the bundled agent CLI and preconfigures it to use ZCP in the service. +- **Cloud IDE** - optional browser-based VS Code in remote setup. +- **`zcp` binary** - the executable. In remote setup it is inside the `zcp@1` service; in local setup you install it and run `zcp init` in a project folder. +For the full vocabulary, including `zcp`, zCLI, zsc, MCP, runtime layouts, and credential names, see the [Glossary](/zcp/glossary). + +---------------------------------------- + +# Zcp > Quickstart + +Remote setup is the fastest first run: no local `zcp` install, no local MCP config, and the bundled agent is already connected to the project. You will open a project with **Include Coding Agent** enabled, give the agent a small app request, and inspect what it verified. +## Prerequisites +- A Zerops account with permission to create a project. +- A login for the bundled agent shown in the dashboard. +## Create a remote setup project +The fastest path is a recipe with the **AI Agent** environment: +1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes). +2. Pick a recipe that matches the stack you want to try. +3. Select the **AI Agent** environment. +4. Deploy the recipe. +The **AI Agent** environment creates the app services plus a `zcp@1` remote setup with **Include Coding Agent** enabled. Keep **Cloud IDE** enabled too so you can open the browser workspace. If you start from an empty project instead, enable **Add Zerops Control Plane (ZCP) service** during project creation and keep **Include Coding Agent** on; the agent can use existing app services or create the missing ones from your prompt. Full setup path: [Set up remote ZCP](/zcp/setup/hosted-workspace). +## Open remote setup +When provisioning finishes, open the `zcp` service in the Zerops dashboard. If **Cloud IDE** is enabled, open its workspace URL; a browser editor opens with the agent connected to this project through ZCP. +Ask for what the app should do. Service setup, deploy, verification, and reporting are part of the ZCP-backed agent contract. +In the agent chat: +```text +Build a task board where tasks stay saved after refresh. +``` +Add constraints only when they change the product, runtime layout, or delivery path: +```text +Build a task board where tasks stay saved after refresh. Use the existing dev+stage pair if this project has one. +``` +```text +After it works, set up git-push delivery to git@github.com:my-org/task-dashboard.git. +``` +## What you should see +A good run should make these points clear: +1. The agent read current project state before changing anything. +2. It named the app runtime it will change. The `zcp` service is the ZCP setup, not the app target. +3. It used existing services when they already fit, or created missing services before app work. +4. It changed code and `zerops.yaml` as needed. +5. It deployed through Zerops and read logs or events if something failed. +6. It verified both platform reachability and the requested dashboard behavior. +7. It ended with a URL or a concrete blocker. +Open the URL the agent gives you. If the page loads but the requested behavior is missing, the task is not done; ask the agent to verify that exact behavior again. +## Next steps +- [How ZCP works](/zcp/concept/how-it-works) - the model behind the run. +- [Build with ZCP](/zcp/workflows/build-with-zcp) - how real app work flows. +- [Choose how finished work ships](/zcp/workflows/delivery-handoff) - direct deploy, git-push, or external handoff. +## Gotchas +- **Deploy success is not done.** The app behavior you requested must be verified on the real URL or endpoint. +- **The `zcp` service is not the app.** Runtime services such as `appdev`, `appstage`, or `app` receive app code and deploys. +- **Production stays out of the agent loop.** Use ZCP in dev/staging projects; promote to production through your CI or release process. + +---------------------------------------- + +# Zcp > Reference > Agent Workflow + +Exact vocabulary for ZCP workflows: runtime layout, verification, delivery, failure state, and completion evidence. Day-to-day app prompts should use outcomes; policies and handoffs can use these names when precision matters. +## Session layers +| Layer | What it is | What changes here | +|---|---|---| +| ZCP setup | Where ZCP runs: remote `zcp@1` service or local `zcp` binary. | No app code should be deployed to the setup itself. | +| Target runtime service | The app runtime: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys. | +| Managed services | Databases, caches, queues, search, storage. | Schema/data operations, never app code deploys. | +| Managed services | Databases, caches, queues, search, storage. | Schema/data operations, never app code deploys. | +The `zcp` service is the control surface, not the app runtime. +## Service setup +Before code work starts, ZCP should identify the app runtime services and managed dependencies. It should use existing services when they fit, create missing services when the project is empty, and resume from live project state if setup was interrupted. +The service setup phase stops before app edits begin; the full ZCP work session then continues into code, deploy, verification, and recovery. +## App work completion contract +For an app intent, ZCP expects the agent to handle code, config, deploy, verify, and retries behind the prompt. A completed session leaves evidence of: +- runtime scope, +- app code and `zerops.yaml` changes, +- a direct deploy for the first verified runtime, +- platform reachability, +- requested behavior on the real endpoint or UI, +- evidence-based retries when something failed, +- a URL or blocker. +## Verification layers +| Layer | What it proves | +|---|---| +| Runtime reachability | Service status, recent error logs, and HTTP probe for eligible runtime services. | +| Requested behavior | The user-requested endpoint, UI state, job result, database state, or stage URL works. | +| Requested behavior | The user-requested endpoint, UI state, job result, database state, or stage URL works. | +Both layers must pass before a session reports completion. +## Runtime layouts +| Layout | Meaning | +|---|---| +| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage must be named when included. | +| `dev` | One mutable development runtime. | +| `simple` | One runtime with no dev/stage split. | +| `local-stage` | Local checkout linked to one Zerops runtime for deploys. | +| `local-only` | Local checkout with no linked deploy target yet. | +| `local-only` | Local checkout with no linked deploy target yet. | +Runtime layout describes which app runtime services ZCP should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. +## Delivery choice +Delivery choice applies after a verified deploy and describes future changes. +| Choice | Meaning | +|---|---| +| Direct deploy | ZCP keeps deploying changes directly to the target runtime. | +| Git push | The agent commits and pushes to a configured remote; any resulting build still needs verification. | +| External handoff | Your CI, release process, or a human owns future delivery. | +| External handoff | Your CI, release process, or a human owns future delivery. | +## Failure categories +| Category | Meaning | Read first | +|---|---|---| +| `build` | Build phase failed. | Build logs and build commands. | +| `start` | Build passed, runtime failed to start or crashed. | Runtime/prepare logs and start command. | +| `verify` | Runtime exists but reachability or behavior failed. | Failing check detail and runtime logs at request time. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and network path. | +| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Structured rejection detail and config files. | +| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | +| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +## Completion evidence +A well-run session can answer: +- which services were used or created, +- which runtime scope was changed, +- which deploy passed, +- which reachability and behavior checks passed, +- which URL or endpoint proves the result, +- which delivery contract applies next, +- or which blocker remains and what evidence supports it. +Operation-name reference lives in [Advanced operations](/zcp/reference/mcp-operations). Recovery shortcuts live in [Troubleshooting](/zcp/reference/troubleshooting). + +---------------------------------------- + +# Zcp > Reference > Index + +Exact terms, operation names, and recovery detail live here. Day-to-day app work starts in [Build with ZCP](/zcp/workflows/build-with-zcp). +| Need | Page | +|---|---| +| Name the workflow phases, runtime layouts, verification layers, and delivery choices | [Workflow terms](/zcp/reference/agent-workflow) | +| Look up common MCP operation names for agent-client policy or debugging | [Advanced operations](/zcp/reference/mcp-operations) | +| Turn a running service into a re-importable bundle | [Package a running service](/zcp/workflows/package-running-service) | +| Recover a stuck or confused session | [Troubleshooting](/zcp/reference/troubleshooting) | +| Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | +| Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | + +---------------------------------------- + +# Zcp > Reference > Mcp Operations + +Most users never need operation names. They matter when you configure agent-client policy, debug an MCP integration, or automate ZCP directly. In normal app work, ask the agent for outcomes. Recipe-authoring and ZCP-internal maintenance operations are intentionally out of scope. +## Permission classes +| Class | Meaning | +|---|---| +| Read-only | Inspect state, logs, events, guidance, or verification output. | +| Mutating | Change services, env vars, scaling, deploy state, or lifecycle. | +| Operational setup | Coordinate workflows, mounts, imports, recipes, delivery setup, or generated local files. | +| Operational setup | Coordinate workflows, mounts, imports, recipes, delivery setup, or generated local files. | +## Read-only operations +| Operation | Purpose | +|---|---| +| `zerops_discover` | Read services, ports, env-var keys, and current state. | +| `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | +| `zerops_events` | Read service-scoped project activity, deploys, builds, scaling, and failures. | +| `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | +| `zerops_knowledge` | Fetch ZCP guidance or platform knowledge for the current state. | +| `zerops_process` | Check a known async process; cancel is a mutating action. | +| `zerops_process` | Check a known async process; cancel is a mutating action. | +## Mutating operations +| Operation | Purpose | +|---|---| +| `zerops_deploy` | Ship code through the Zerops build and deploy pipeline. | +| `zerops_env` | Read, set, delete, or generate env vars and local `.env` files. | +| `zerops_manage` | Start, stop, restart, reload, or connect storage. | +| `zerops_scale` | Change CPU, RAM, disk, CPU mode, or container autoscaling where supported. `HA`/`NON_HA` is set at service creation. | +| `zerops_subdomain` | Enable or disable public subdomain access. | +| `zerops_delete` | Delete a service. Explicit named approval is required. | +| `zerops_delete` | Delete a service. Explicit named approval is required. | +## Operational setup +| Operation | Purpose | +|---|---| +| `zerops_workflow` | Track, recover, or configure ZCP work sessions; also carries the package-running-service export flow. | +| `zerops_import` | Import project/service definitions. Destructive override is gated. | +| `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | +| `zerops_preprocess` | Expand Zerops preprocessor expressions. | +| `zerops_export` | Read platform project/service export YAML and service metadata. | +| `zerops_export` | Read platform project/service export YAML and service metadata. | +## Remote and local differences +| Area | Remote setup | Local setup | +|---|---|---| +| Files | Runtime files through SSHFS/project containers | Local working directory | +| Deploy source | Project service or batch deploy inside Zerops | Local `workingDir` | +| Dev server | ZCP can run/inspect project-container dev processes | Your local tool owns the dev server | +| Env bridge | Project env is available in the project | `.env` generation resolves project references for local app use | +| Git | Workspace-managed credentials | User's local git credentials | +| Git | Workspace-managed credentials | User's local git credentials | +## Confirmation gates +Two operations require explicit care: +- **Service deletion** requires explicit user approval in the current conversation, by service name. +- **Destructive import override** first refuses and names what would be replaced; a second call must acknowledge the same targets. +See [Tokens and credentials](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions) for the user-facing confirmation flow. + +---------------------------------------- + +# Zcp > Reference > Troubleshooting + +Start recovery from current project state, not from chat memory. +```text +Read ZCP status and tell me where this project stands before changing anything. +``` +That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. +## Failure categories +| Category | Meaning | First move | +|---|---|---| +| `build` | Build failed before a runtime could start. | Read build logs and `zerops.yaml` build steps. | +| `start` | Build passed, runtime failed to start or crashed. | Read runtime/prepare logs and check start command, ports, and env vars. | +| `verify` | Runtime exists, but reachability or requested behavior failed. | Read the failing check, HTTP response, and runtime logs at request time. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Check the named network path and, for local setup, VPN. | +| `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Read the structured rejection and fix the named field. | +| `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | Fix the named credential; do not rotate unrelated tokens. | +| `other` | No known category matched. | Read raw events/logs and stop if the same unknown failure repeats. | +| `other` | No known category matched. | Read raw events/logs and stop if the same unknown failure repeats. | +## Common symptoms +| Symptom | Likely move | +|---|---| +| Build log shows missing command/module/lock file | Fix build commands, manifests, deploy files, or dependencies. | +| Runtime is `READY_TO_DEPLOY` or restarts continuously | Read runtime logs; the start command or env contract is usually wrong. | +| Public URL returns 502 or connection error | Check port binding, `0.0.0.0`, subdomain readiness, and whether the service is HTTP-eligible. | +| Deploy succeeded but feature is broken | Treat it as behavior verification failure, not deploy success. | +| Local app cannot reach `db`, `redis`, or storage | Bring VPN up and regenerate `.env` if project env changed. | +| Agent targets `zcp` as the app | Correct it to the runtime service. | +| Agent loops on the same failure | Ask for category, evidence read, fixes attempted, and the next human decision. | +| Agent loops on the same failure | Ask for category, evidence read, fixes attempted, and the next human decision. | +Public subdomain access is enabled on first deploy for eligible HTTP runtimes. Workers and non-HTTP services do not get a useful public URL by design. +## When to stop +Stop the loop when the agent cannot make progress with new evidence, needs a missing credential, is about to delete or replace a service, or the target runtime/stage is ambiguous. +Before destructive recovery, read service-scoped events, logs, deploy/verify result, and git history when delivery uses git-push. +## Related +- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) +- [Workflow terms](/zcp/reference/agent-workflow) +- [Tokens and credentials](/zcp/security/tokens-and-project-access) +- [Set up local ZCP](/zcp/setup/local-agent-bridge) + +---------------------------------------- + +# Zcp > Security > Production Policy + +Rule: keep ZCP in a development or staging Zerops project. Production should be a separate project without a `zcp` service; promotion happens through CI or a release pipeline with production-scoped credentials. +The platform doesn't stop you from adding a `zcp` service to a production project. Treat the dev/staging boundary as load-bearing because crossing it puts an agent inside production, not because Zerops will refuse the configuration. +## Why a separate production project +The clean separation is the project boundary: ZCP runs where the agent works, while production runs in its own 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 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 with no `zcp` service, 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 architecture pattern production uses — same runtime version, same managed service types, same build/deploy path — before promotion to the separate production project. +Use stage as the production-like rehearsal: +- The agent develops on dev (`appdev` in the `standard` runtime layout). +- 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-like 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 operational access to 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-like 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 ZCP setup | +| Run the agent's deploys directly into production from a development session | Production deploys come from the production project's pipeline, not the development ZCP setup | +## 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. +- [Troubleshooting](/zcp/reference/troubleshooting) — recover or take over before promoting. +- [Backup](/features/backup) — the production data safety net that operates outside ZCP. +- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) — the dev/stage loop this policy assumes. + +---------------------------------------- + +# Zcp > Security > Tokens And Project Access + +ZCP enforces the project boundary at three layers: token shape (one project, exactly), where the token lives (platform-injected for remote setup, in `.mcp.json` for local setup), and confirmation gates on operations that are not reversible from inside the conversation. +## Recommended token shape — one project, full access +ZCP requires a Zerops API token that resolves to **exactly one project** at startup. For normal agent workflows, use **Custom access per project** scoped to a single project with **Full access**. Read-only tokens can pass startup, but they fail as soon as the agent tries to deploy, write env vars, or operate services. +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 wrong shapes before the agent can operate the project. +| Token shape | What ZCP does | Why | +|---|---|---| +| Multi-project, including account-wide full access | Refuses to start | One ZCP process equals one project; ZCP won't pick which one. | +| No project access | Refuses to start | 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 to start | The token no longer authenticates against the Zerops API. | +| Expired or revoked | Refuses to start | The token no longer authenticates against the Zerops API. | +The fix is always the same: generate a per-project token and replace `ZCP_API_KEY` on the surface ZCP reads from. In remote setup, that value is injected into the `zcp` service environment. In local setup, it is the `env` block in `.mcp.json`. +## Where the token lives - remote vs local {#where-the-token-lives--remote-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: +| Setup | Where `ZCP_API_KEY` comes from | Who provisions it | +|---|---|---| +| Remote setup | The `zcp` service container env | Zerops platform - injected automatically when the service boots | +| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You - added manually after `zcp init` | +| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You - added manually after `zcp init` | +In remote setup, the service starts with `ZCP_API_KEY` populated. If **Include Coding Agent** is enabled, the bundled agent uses that environment automatically. +In local setup, `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 itself, against the Zerops API | Container env (remote) or `.mcp.json` env block (local) | +| `GIT_TOKEN` | A git push from remote setup to a remote (GitHub, GitLab) | Remote: secret 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 | +| `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 remote setup is the one pushing. In local setup, 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 `zcp` service 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** — an 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. Delivery setup should place it in the CI secret store for the repository that runs the deploy. +## Rotation +Rotate in the Zerops dashboard, then propagate to the consuming surface: +- **Remote setup** — the service env value updates; ZCP picks up the new value the next time the `zcp` service boots or restarts. +- **Local setup** — paste the new token into the `env` block of `.mcp.json`, then restart your agent client 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. | Requires explicit approval in the same conversation, including the service name. Remote setup also blocks deleting the `zcp` service it is running in. | +| **Wholesale service replacement** from an import on a service with prior failed deploy history | First returns a summary of what would be replaced and refuses to continue. The agent must show that evidence before asking you to confirm. | +| **Wholesale service replacement** from an import on a service with prior failed deploy history | First returns a summary of what would be replaced and refuses to continue. The agent must show that evidence before asking you to confirm. | +ZCP itself adds confirmation only for the two operations above. Your agent client or team policy may still prompt before tool calls. Deploys, env changes, lifecycle actions, restarts, scaling, and public-access changes do not get an additional ZCP-specific confirmation gate. Most are reversible by another call. A few are operationally destructive even though they are not gated by ZCP (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. +- **Replace-as-reset** — replacing a service from an import 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. +Threat model and hard refusals: [Trust model](/zcp/security/trust-model). +## Gotchas +- **Multi-project tokens are refused at startup, not at first deploy.** An account-wide 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 in remote setup; `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), and review service-scoped events, logs, deploy results, and git history before approving destructive recovery. +## 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. +- [Set up local ZCP](/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`. + +---------------------------------------- + +# Zcp > Security > Trust Model + +The security model starts with one rule: a ZCP process operates one Zerops project. Remote and local setup share that project boundary, but they expose different surroundings to the agent. +## Remote setup or local setup +**Remote setup** is the project-contained path. +- ZCP runs in a `zcp@1` service inside the Zerops project. +- When **Include Coding Agent** is enabled, the bundled agent CLI runs there and is preconfigured to use ZCP. +- It uses a project-scoped token and reaches the project's private network. +- Runtime files are visible only when they are mounted into remote setup. +- Safety profile: **project-contained by design** - no direct access to your laptop, home directory, or other projects. +**Local setup** is the supervise-the-client path. +- The agent runs on your laptop. +- It can touch the same project surface, **plus whatever the agent client can reach on your laptop**. +- It reaches project services over VPN, plus everything else your laptop can already reach. +- Safety profile: **supervise the agent client** - ZCP stays project-scoped, but the agent process inherits your user. +Either path is the right choice in context. Local setup 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 exposing operations. A token with access to no projects is refused; a token with access to multiple projects is refused. The intended token form is 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). | +| 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. +## Remote setup specifics +Remote setup is the project-contained path. A few specifics: +- **`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. +- **Project access stays inside the project.** The `zcp@1` service can read project env and reach project services so the agent can build against real dependencies without you copying credentials around. +- **Filesystem reach is narrower than network reach.** The agent reaches project services over the private network, but only sees runtime files when they are mounted into remote setup. +- **A terminal in remote setup has the same project-scoped access as the agent.** Convenient, and the reason production stays in a separate project. +## Local setup specifics +Local setup 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; the binary doesn't merge local state across them. +- **VPN setup is outside ZCP's authority.** Bringing up the Zerops VPN requires admin or root approval on your laptop. ZCP can tell 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. +- **Local setup 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: [Set up local ZCP](/zcp/setup/local-agent-bridge). +## Audit surface +Zerops records platform-side evidence: service events, deploy/build results, runtime logs, lifecycle actions, scaling, and public-access changes. It does not record the agent's private reasoning, every shell edit, browser-helper actions, or prompt history outside your agent client. +When taking over from an agent, read evidence in this order: service-scoped events, runtime logs, deploy/verify result, then git history when delivery uses git-push. +## What ZCP refuses by design +ZCP refuses boundaries that should never be inferred: tokens that do not resolve to exactly one project, remote self-deletion, and destructive replacement before the agent has read the relevant failure evidence. Destructive confirmation details live in [Tokens and credentials](/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. +- [Troubleshooting](/zcp/reference/troubleshooting) — recover when the agent or session state drifts. +- [Production boundary](/zcp/security/production-policy) — keep production outside the agent loop. + +---------------------------------------- + +# Zcp > Setup > Choose Workspace + +Choose where the ZCP-backed agent runs. ZCP gives the agent the same project-scoped Zerops toolset in both paths, but the filesystem, network path, deploy source, and safety profile are different. +Use remote setup for the first run and project-contained isolation. Use local setup when your checkout, editor, dev server, and git credentials should stay on your machine. +## Short rule +**Remote setup** is the default first run: no local install, project-contained isolation, optional bundled agent, optional browser IDE. +**Local setup** fits when you want to keep your editor, shell, checkout, local dev server, and git credentials. +## Compare the paths +**Remote setup** +- ZCP runs inside the Zerops project in a `zcp@1` service. +- The agent runs inside that service when **Include Coding Agent** is enabled. +- Runtime files can be mounted through SSHFS. +- Managed services are reached over the project-private network. +- Deploy source, env vars, and git credentials live inside the project service. +- Safety profile: project-contained by design. +**Local setup** +- ZCP runs on your laptop as the `zcp` binary. +- The agent runs in your local editor or CLI after `zcp init`. +- App work happens in your local working directory. +- Managed services are reached from your app and shell through Zerops VPN. +- Deploy source and git credentials are your local working directory and local git setup. +- Safety profile: ZCP is project-scoped, but the agent client runs as your user. +## What changes in practice +In remote setup, a `zcp@1` service runs the `zcp` binary inside the Zerops project. Enable **Include Coding Agent** when you want the service to add the bundled agent CLI and preconfigure it to use ZCP. Enable **Cloud IDE** when you also want browser-based VS Code. Runtime files can be mounted into the service, and managed services are reachable over the project-private network. +In local setup, the agent works in your local checkout. You install the `zcp` binary and run `zcp init` in the project folder so the local agent can talk to ZCP. ZCP talks to the Zerops API directly, while your local app and shell need `zcli vpn up` to reach managed services by hostname. +After setup, ZCP reads whether the project has one runtime, a dev+stage pair, or a local checkout linked to a runtime. You usually state the outcome, not the label: dev+stage, one dev runtime, or local deploys to a named runtime. Exact labels live in [Workflow terms](/zcp/reference/agent-workflow#runtime-layouts). +Stage is a review or promotion target, not production. Production should live in a separate Zerops project; see [Production boundary](/zcp/security/production-policy). +## Next steps +- [Set up remote ZCP](/zcp/setup/hosted-workspace) +- [Set up local ZCP](/zcp/setup/local-agent-bridge) +- [Build with ZCP](/zcp/workflows/build-with-zcp) + +---------------------------------------- + +# Zcp > Setup > Hosted Workspace + +Remote setup is a `zcp@1` service inside your Zerops project. It keeps ZCP, project-scoped credentials, private-network access, and optional agent tooling inside the project boundary. +Enable **Include Coding Agent** when you want Zerops to add the bundled agent CLI and preconfigure it to use ZCP in that service. Enable **Cloud IDE** when you also want browser-based VS Code for watching or intervening. +After provisioning, the service is ready for your first app instruction. If **Cloud IDE** is enabled, you can open the browser workspace from the dashboard; otherwise use the access method configured for the service. +Remote and local setup have the same ZCP project boundary but different work surfaces. The comparison lives in [Choose remote or local setup](/zcp/setup/choose-workspace); the broader human development modes live in [Local & Remote Development](/features/local-remote-development). +## Two paths to remote setup +Zerops provisions the service for you - no local install, no MCP config to write by hand, 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 a `zcp@1` service with **Include Coding Agent** enabled. +- **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 base result: a `zcp@1` service inside the project with `ZCP_API_KEY` injected automatically and scoped to this project. **Include Coding Agent** adds the preconfigured agent. **Cloud IDE** adds the browser editor. +## Path A — recipe + AI Agent environment +Recipes are the quickest path when their stack matches the runtime layout 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@1` service with **Include Coding Agent** enabled 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 with a bundled agent is added. 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. Keep **Include Coding Agent** enabled to add and preconfigure the bundled agent inside the service. Use **Configure** when you need to choose authentication or adjust Cloud IDE access; otherwise leave the defaults 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 service +Open the `zcp` service from the Zerops dashboard after it reaches running state. +1. Open the project in the [Zerops dashboard](https://app.zerops.io/). +2. Open the `zcp` service. +3. If **Cloud IDE** is enabled, open the service workspace URL. Code-server launches in a new tab. If the service is exposed on a `.zerops.app` subdomain, see [Public access](/features/access) for the routing model. +If **Cloud IDE** is disabled, remote setup can still exist as a project-contained service, but there is no browser editor to open. Use the access method configured for the service, such as SSH, or enable Cloud IDE when you want a browser workspace. +## Run the agent +When **Include Coding Agent** is enabled, the bundled agent starts already connected to this project through ZCP. In the Cloud IDE path, the terminal has the bundled agent available with `ZCP_API_KEY` in its environment. +For a connection sanity check, ask the bundled agent: +```text +List the services in this project and tell me which are runtimes versus managed dependencies. +``` +A working setup answers with the runtime services (for example `appdev`, `appstage`) and managed services (for example `db`), and says whether the project has one runtime or a dev+stage pair. +For real work, ask for the app behavior, not the Zerops procedure: +```text +Build a task board where tasks stay saved after refresh. +``` +Discovery, service setup, deploy, verification, and final reporting happen behind that app request. +If **Include Coding Agent** is disabled, the `zcp@1` service exists but no preconfigured agent is waiting inside it. Enable the option or use [local ZCP](/zcp/setup/local-agent-bridge) from your own agent client. +After the first setup pass, runtime services can be SSHFS-mounted into the service filesystem with one folder per runtime. 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 remote setup. +If you added ZCP to an existing project, the agent should inspect existing services and use them instead of creating duplicates. That is handled as part of the first product prompt; the details are explained in [Service setup behind the prompt](/zcp/workflows/create-or-adopt-services). +When Cloud IDE is enabled, the browser workspace is persistent. Close the browser tab and reopen the workspace link later; editor state, open files, and the agent session survive across reconnects as long as the `zcp` service is running. +## If connection fails +If the agent can't reach ZCP, the service may still be starting. Wait for it to reach running state in the dashboard and reload the workspace. If **Include Coding Agent** was not enabled, add it or use local ZCP from your own agent client. +At this point, the agent can receive the first app instruction. The [Quickstart](/zcp/quickstart) shows the shortest end-to-end run. +## Customize remote setup +Remote setup is a normal Zerops service, so teams can customize it like one: add tools, preload team config, or run helper processes next to the agent and Cloud IDE. Keep this separate from app runtime work; deploy app code to runtime services, not to `zcp`. +## Gotchas +- **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. If the agent targets `zcp` as the app runtime, stop and correct the target. +- **Don't set `ZCP_API_KEY` by hand in remote setup.** It's platform-injected. Manual override risks breaking the agent's access. (Local setup 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 the bundled agent. Until the service reaches running state, the workspace URL may return an empty page or error. + +---------------------------------------- + +# Zcp > Setup > Local Agent Bridge + +Local setup runs the `zcp` binary on your laptop, initialized in one project directory. The agent edits your checkout, your tools run the dev server, and ZCP deploys from your working directory to the linked Zerops runtime. +`zcp init` writes the local MCP configuration for that folder, so a compatible local agent client can talk to ZCP as the `zerops` MCP server. With `zcli vpn up `, your laptop also joins the project's private network - the same network your services use. +Local setup is newer than remote setup. Expect install details and generated project files to move between releases. +Use this path when you already have a comfortable local setup - your editor, your shell, your dev server - and want a coding agent that can drive Zerops from the same machine. If you're evaluating ZCP for the first time, [remote setup](/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. ZCP is what you add when you also want a coding agent operating the project from the same machine: discovering services, generating local env snapshots, deploying, reading logs, verifying. +Security note: ZCP itself stays project-scoped, but the agent client runs as your local user. Set your client permissions accordingly. +## 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 compatible local agent client installed and logged in. ZCP doesn't see the agent client's subscription or login credentials. +## Get a project-scoped Zerops token +ZCP needs a Zerops API token scoped to **one project**. Multi-project tokens, including account-wide full-access tokens, are refused at startup. Generate one in the Zerops dashboard, then keep the value handy for the local MCP configuration step. Full walkthrough, rejection rules, and rotation: [Tokens and credentials](/zcp/security/tokens-and-project-access). +## Install `zcp` +```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. +## Run `zcp init` in your project folder +In a terminal in your project's working directory: +```bash +zcp init +``` +`zcp init` writes the local MCP configuration and agent instructions for this project. Re-running it may refresh generated config; after rerunning it, confirm `.mcp.json` still contains `ZCP_API_KEY` before launching the agent. +## Configure `zcp` as the local MCP server +`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 your agent client from the 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--remote-vs-local). +The agent client starts and lists the MCP servers it found. The `zerops` server should be in the list; that is the local ZCP connection. +### 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 the agent client from the wrong directory. +## Connect private services with `zcli vpn up` +Your laptop reaches managed services (databases, caches, storage) through the Zerops project [VPN](/references/networking/vpn). ZCP 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. +After the VPN is up, project services resolve by hostname (`db`, `cache`, etc.) from your laptop. +## Local env bridge +When a local app needs project credentials, ask for the product change and let ZCP generate the local env bridge as part of that work. For a standalone sanity check, you can ask: +```text +Use ZCP to generate a .env file for my local app. +``` +ZCP resolves the project env references your app would see when deployed and writes a `.env` snapshot in your working directory. Your local app uses the same hostnames and credentials, reached over VPN. Regenerate after changing project env variables. +## Linked deploy target +Local setup needs one Zerops runtime linked as the deploy target, often a stage runtime, 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. Ask the agent to link a runtime by hostname when ready. +With a linked runtime, ZCP can deploy from your working directory. Without one, it can still inspect the project and generate env snapshots, but deploys need a target runtime first. +## 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. Remote ZCP can give the agent SSHFS access; local ZCP 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; the agent client 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 the agent client both expect it. +- **The `.env` is a snapshot.** Regenerate after changing project env variables, not before. +- **Production stays out of this loop.** Local ZCP is for development and staging projects. +## Next steps +- [Deploy, verify, and fix](/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 remote or local setup](/zcp/setup/choose-workspace) - remote vs local and runtime layouts. + +---------------------------------------- + +# Zcp > Workflows > Build And Verify App + +After the target runtime and dependencies are known, the agent can change code and `zerops.yaml`, deploy the runtime, fix failures from evidence, and verify the result. +A ZCP task is done only when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the working URL or a concrete blocker. +## What the agent does +ZCP treats deploy as diagnostics and verification as the completion gate. The user does not have to script this sequence; it is the expected path behind an app prompt. +- **Runtime scope.** The agent identifies which runtime changed. +- **Code and `zerops.yaml`.** The agent changes app files and runtime config where needed. +- **First deploy.** The first verified runtime deploy goes through ZCP. +- **Runtime reachability.** The agent checks that the runtime answers on the real platform path. +- **Requested behavior.** The agent proves the endpoint, URL, UI state, row, job result, or other acceptance evidence. +- **Evidence-based fix.** If either verification layer fails, the agent reads new logs/events/check output, fixes the cause, and redeploys. +A green build or running runtime is not done until the requested endpoint, UI, or state is checked on the real URL. Retries should use new evidence, not repeat the same deploy. +## Runtime scope is a precondition +Before editing, the agent should make the runtime service visible: `appdev`, `appstage`, `app`, or the linked runtime in a local setup. Managed services such as databases and caches are dependencies, not deploy targets. +If the agent starts writing files before it knows the runtime scope, treat that as a correction signal: it should read ZCP status and identify the target before continuing. +## Deploy +The first deploy uses the direct ZCP deploy path. Delivery choices such as git-push or external handoff apply after a verified deploy exists. +Deploy failures point to different evidence: +| Failure point | Read first | +|---|---| +| Build failed | Build logs and `zerops.yaml` build steps | +| Runtime started then crashed | Runtime logs and start command | +| App is up but route fails | Runtime logs at request time and behavior check output | +| Local app cannot reach managed services | VPN state and generated `.env` values | +| Local app cannot reach managed services | VPN state and generated `.env` values | +## Verify +Verification has two layers: +| Layer | What it proves | +|---|---| +| Platform reachability | The service is running, recent error logs are checked, and HTTP runtime services answer a probe when eligible. | +| Requested behavior | The thing you asked for works: endpoint body, UI state, database row, job result, or stage URL. | +| Requested behavior | The thing you asked for works: endpoint body, UI state, database row, job result, or stage URL. | +Both layers must pass before the agent reports completion. +## Fix or stop +The agent should name the failure point in plain language, read fresh logs/events/check output, fix the indicated cause, and redeploy. A repeated retry with no new evidence is not progress. +Stop and ask for a decision when scope is ambiguous, credentials are missing, destructive recovery is needed, or several evidence-based retries hit the same failure. +## Review and take over +The platform records deploys, events, runtime logs, and lifecycle actions. It does not record every agent thought, browser click, or shell edit. When taking over, ask for ZCP status first, then read service-scoped events, logs, deploy/verify results, and git history if delivery uses git-push. +## Gotchas +1. **Deploy success is not verify success.** A green build with a broken feature is not complete. +2. **Stage is explicit.** In dev+stage projects, the agent should say when stage is included before changing or verifying it. +3. **Secrets belong in env vars.** If the app needs a third-party API key, the agent should ask for an env-var path, not put secrets in source. + +---------------------------------------- + +# Zcp > Workflows > Build With Zcp + +Start with what the app should do, not with tool calls or workflow names. +```text +Build a task board where tasks stay saved after refresh. +``` +Behind that prompt, the agent should inspect the project, choose a target runtime, make the app change, and use real deploy evidence until the requested behavior is proven. ZCP supplies the project knowledge and tool access needed for that loop: live services and env state, Zerops guidance, deploys, logs, verification, and guarded infrastructure changes. +Do not make the prompt longer to restate completion expectations such as deploy, verification, or returning the URL. The words you add should change the app, the stack, the runtime layout, the acceptance criteria, or the delivery path. +You also do not need to paste an infrastructure inventory, env wiring plan, or log summary into the prompt. ZCP makes those available to the agent from the live project. +## What happens after your prompt +The agent should make this path visible as it works: +**Project discovery.** Read what exists now: runtime services, managed services, env vars, logs, recent events, and current work state. +**Service setup.** Use existing services when they fit, or create the missing runtime and managed dependencies before app code starts. +**Runtime scope.** Identify the app runtime that will receive code, such as `appdev`, `appstage`, `app`, or a linked local deploy target. The `zcp` service is never the app target. +**Code, deploy, verify, fix.** Change app files and `zerops.yaml`, deploy through Zerops, verify reachability and requested behavior, read evidence on failure, and retry from the cause. +**Delivery after proof.** After a verified deploy exists, keep direct deploy, push to git, or use external handoff to CI or a human. +Advanced export/reuse path: [Package a running service](/zcp/workflows/package-running-service). +## What the final answer should contain +For a completed app task, the agent should report: +- which runtime service changed, +- which deploy passed, +- which URL, endpoint, or UI state proved the requested behavior, +- any env vars, managed services, or delivery settings touched, +- the delivery choice if one was set. +If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the next decision needed from you. + +---------------------------------------- + +# Zcp > Workflows > Create Or Adopt Services + +When an app prompt needs infrastructure, ZCP should discover what already exists before creating runtimes or managed services. This usually happens because you asked for a product outcome, not because you explicitly asked for infrastructure. +For example, `Build a task board where tasks stay saved after refresh.` may require an app runtime and database. `Use the existing Laravel services` tells the agent to reuse what is already there instead of creating duplicates. +Behind that prompt, the agent should answer: +- Which runtime service will hold app code? +- Which managed services does the app depend on? +- Do those services already exist, or should the agent create them? +- Are they running and visible to ZCP? +This part does **not** write application code, create `zerops.yaml`, run the first deploy, or verify behavior. Those belong to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). +## Starting situations +| Situation | What should happen | +|---|---| +| Runtime services already exist | Use them. Identify runtimes and managed dependencies. Do not recreate or rename services just to begin. | +| Project has only the `zcp` service | Treat it as empty from the app point of view. Create or import the requested service layout. | +| Project is empty and request matches a known stack | A recipe may be a good starting base, but the starter still has to be changed into the requested app. | +| Project is empty and request needs a custom layout | Create a custom service plan before app work starts. | +| Setup was interrupted | Resume from current ZCP status instead of starting over. | +| Setup was interrupted | Resume from current ZCP status instead of starting over. | +The `zcp` service is the ZCP setup, not the application runtime target. +## Prompt examples +Product prompts can imply the service setup; they do not need to spell it out: +```text +Build a Node.js API with PostgreSQL. Use the existing dev+stage pair if this project has one. +``` +```text +Use the existing Laravel services and build a small notes app. +``` +```text +Add saved tasks to this task board. +``` +The agent should explain whether it used existing services or created new ones, then move into app work when infrastructure is known. +## Done state +Service setup is done when the app runtime and managed dependencies are known, any new services are visible, and the agent is ready to start app code, `zerops.yaml`, deploy, and verification work. +When files, framework setup, deploys, logs, or behavior checks appear, the work has moved to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). +## Failure signals +| Symptom | What to do | +|---|---| +| Agent targets `zcp` as the app | Correct the target to the runtime service. | +| Agent recreates existing runtimes | Ask it to inspect current services and use what exists. | +| Agent writes app files during service setup | Stop and finish infrastructure first, or move explicitly to app work. | +| Service creation fails | Treat it as a hard stop until the failure is understood. | +| Service creation fails | Treat it as a hard stop until the failure is understood. | + +---------------------------------------- + +# Zcp > Workflows > Delivery Handoff + +Delivery is decided after the app works. You can include delivery intent in the original product prompt, but ZCP applies it only after a verified deploy exists. The first deploy uses ZCP's direct deploy path; the delivery choice records how future changes should be delivered. +## Three delivery choices +**Keep direct deploy** for fast iteration, solo projects, and demos. The agent keeps deploying through ZCP directly. +**Push to git** when you want commits, reviews, or a repository-driven build. The agent commits and pushes to a configured remote; a build integration may run afterward. +**External handoff** when your CI, release process, or a human owns delivery. ZCP records what happened but does not initiate future deploys. +You can ask in plain language: +```text +After the app verifies, set up git-push delivery to git@github.com:my-org/task-dashboard.git. +``` +```text +Keep ZCP deploying directly for now. +``` +```text +My CI takes over after this verified deploy. +``` +## Push to git +Git-push needs committed code, a remote URL, and credentials that can push. +Remote and local setup differ: +- Remote setup uses credentials configured inside the `zcp` service, commonly a fine-grained git token. +- Local setup uses your local git credentials and working directory. +A push is not proof that the app works. If a webhook or GitHub Actions build runs from the push, the agent should observe the build result and verify the app afterward. +## Change later +Delivery choice is not permanent. You can move from direct deploy to git-push later, add a build integration later, or switch to external handoff for a release that needs human control. +## Gotchas +1. **Delivery choice is not a hidden deploy button.** It records the future path after the app already works. +2. **External handoff is not a deploy by itself.** It means a human or external system owns delivery. +3. **GitHub Actions uses a Zerops token.** The Actions secret for `zcli` deploys is `ZEROPS_TOKEN`, not your GitHub token. + +---------------------------------------- + +# Zcp > Workflows > Package Running Service + +Use packaging when a deployed runtime should become a reusable import bundle. This is an advanced handoff or reuse workflow, not the normal way to deploy the next change. 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 layout 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 the next app change deployed. For that, ask for the app result; direct deploy is the expected ZCP path behind the request. +- You want to deploy the next change to an existing production project. Keep production in its own Zerops project and operate it through Zerops UI, the API, or CI. +Packaging does not keep a long-running session. If interrupted, the agent reads live state and re-runs the export flow; you only decide when the dev or stage runtime, or env classification, is ambiguous. +## 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 is chosen, 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. | +| `plain-config` | Literal runtime config (`LOG_LEVEL`, `NODE_ENV`, feature flags) | The literal value, verbatim. | +ZCP does not guess these buckets from key names alone. The agent should classify them from source code: if the value is read by a Stripe SDK call it is `external-secret`; if it appears as `${db_*}` from a managed service it is `infrastructure`. The packaging response carries env *keys* and redacts values; the agent fetches values only 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 +#zeropsPreprocessor=on +# zerops-project-import.yaml — paste into a fresh Zerops project +project: + name: demo + envVariables: + APP_KEY: )> + 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 [Zerops preprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `` directives (here `)>` for `APP_KEY`), `#zeropsPreprocessor=on` must be 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). +Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen handoff path expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through the normal [handoff path](/zcp/workflows/delivery-handoff). +## 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 | +| Production config | Keep production in a separate Zerops project | +## Gotchas +1. **Packaging is not the delivery path by itself.** It produces the bundle. Commit and push happen through your chosen [handoff path](/zcp/workflows/delivery-handoff). +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. +- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) — the verified runtime this packages. +- [How ZCP works](/zcp/concept/how-it-works) — why packaging stays stateless. + +---------------------------------------- + # Zerops Yaml > Base List This is a list of all currently supported versions of technologies that can be used for build.base and run.base sections in `zerops.yaml`. @@ -22520,90 +22529,63 @@ This is a list of all currently supported versions of technologies that can be u Versions listed on the same line are aliases of the same underlying version. ::: ## Runtime services - Service Type Supported OS Versions - Build / Runtime - Bun `ubuntu` / `alpine` - Deno `ubuntu` - .NET `ubuntu` / `alpine` - Elixir `ubuntu` / `alpine` - Gleam `ubuntu` - Go `ubuntu` / `alpine` - Java `ubuntu` / `alpine` - Node.js `ubuntu` / `alpine` - Python `ubuntu` / `alpine` - Rust `ubuntu` / `alpine` -     Build Runtime - PHP + Apache `ubuntu` / `alpine` - PHP + nginx `ubuntu` / `alpine` - ## Static services - Service Type Supported OS Versions - Build Runtime - nginx `ubuntu`/`alpine` - - static `ubuntu`/`alpine` - - ## Containers and virtual machines - Service Type Supported OS Versions - Build Runtime - + Docker + `alpine` + - Alpine `alpine` - Ubuntu `ubuntu` - - Docker - `ubuntu` - - - ---------------------------------------- @@ -22637,7 +22619,7 @@ Specifies the directory where the command will be executed. If not set, it defau ## Example Configurations Here’s a basic example of how to set up a cron job in your service's `zerops.yaml`: ```yaml -run: +run: crontab: - command: "date >> /var/log/cron.log" timing: "0 * * * *" @@ -22646,7 +22628,7 @@ This configuration logs the current date to `/var/log/cron.log` every hour. ### Running on Multiple Containers By default, cron jobs run on a single container, even if multiple containers exist for the service. To execute a command across all containers, you can use the `allContainers` parameter: ```yaml -run: +run: crontab: - command: "rm -rf /tmp/*" timing: "0 0 * * *" @@ -22656,7 +22638,7 @@ This example removes temporary files from all containers every day at midnight. ### Custom Working Directory You can also specify a custom working directory for your commands using the `workingDir` parameter: ```yaml -run: +run: crontab: - command: "php artisan schedule:run" timing: "* * * * *" @@ -22729,12 +22711,12 @@ zerops: ``` Each service configuration requires a `run` section. Optional `build` and `deploy` sections can be added to further customize your process. ## Service Configuration -### setup +### setup Contains the hostname of your service (must exist in Zerops). ```yaml setup: app ``` -### extends +### extends The `extends` key allows you to inherit configuration from another service defined in the same `zerops.yaml` file. This is useful for creating environment-specific configurations while maintaining a common base. ```yaml zerops: @@ -22764,8 +22746,8 @@ When using `extends`: :::tip Create a base service with common configuration and extend it for environment-specific services to keep your `zerops.yaml` file DRY (Don't Repeat Yourself). ::: -## Build Configuration -### base +## Build Configuration +### base Sets the base technology for the build environment. [See available options](/zerops-yaml/base-list). ```yaml build: @@ -22779,7 +22761,7 @@ build: prepareCommands: - zsc add python@3.9 ``` -### os +### os Sets the operating system for the build environment. Options: - `alpine` (default) - `ubuntu` (default for ubuntu service) @@ -22790,7 +22772,7 @@ Current versions: build: os: ubuntu ``` -### prepareCommands +### prepareCommands Customizes the build environment by installing additional dependencies or tools. ```yaml build: @@ -22801,7 +22783,7 @@ build: :::note `build.prepareCommands` run in the `/home/zerops` directory. ::: -### buildCommands +### buildCommands Defines the commands to build your application. ```yaml build: @@ -22819,7 +22801,7 @@ buildCommands: npm install npm run build ``` -### deployFiles +### deployFiles Specifies which files or folders to deploy after a successful build. ```yaml build: @@ -22863,16 +22845,16 @@ This example above ignores `file.txt` in ANY directory named `src`, such as: :::note `.deployignore` file also works with `zcli service deploy` command. ::: -### cache +### cache Defines which files or folders to cache for subsequent builds. ```yaml build: cache: node_modules ``` For more information, see our detailed [guide on build cache](/features/build-cache), complete with extensive examples. -### addToRunPrepare +### addToRunPrepare Defines files or folders to be copied from the build container to the prepare runtime container. -### envVariables +### envVariables Sets environment variables for the build environment. ```yaml build: @@ -22885,8 +22867,8 @@ build: :::info The `yamlPreprocessor` option in your project & service import YAML allows you to generate random secret values, passwords, and public/private key pairs. For more information, see the [yamlPreprocessor](/references/import-yaml/pre-processor) page. ::: -## Deploy Configuration -### temporaryShutdown +## Deploy Configuration +### temporaryShutdown Controls the container replacement order during deployment. ```yaml deploy: @@ -22896,7 +22878,7 @@ deploy: - Default: `false` **When `false` (default):** New containers are started before old containers are removed, ensuring zero-downtime deployment. **When `true`:** Old containers are removed before new containers are started, causing temporary downtime but using fewer resources during deployment. -### readinessCheck +### readinessCheck Defines a readiness check for your application. Requires either `httpGet` object or `exec` object. ```yaml deploy: @@ -22915,23 +22897,23 @@ Readiness checks work similarly to [health checks](#healthcheck-) but are specif Available parameters: #### httpGet and exec The `httpGet` and `exec` options work the same way as in [health checks](#healthcheck-). See that section for detailed parameter descriptions. -#### Common parameters +#### Common parameters The following parameters can be used with either `httpGet` or `exec` readiness checks: - **failureTimeout** - Time in seconds until container is marked as failed. - **retryPeriod** - Time interval in seconds between readiness check attempts (equivalent to `execPeriod` in health checks). :::tip Unlike health checks which run continuously, readiness checks only run during deployments to determine when your application is ready to accept traffic. ::: -## Runtime Configuration -### base +## Runtime Configuration +### base Sets the base technology for the runtime environment. If not specified, the current version is maintained. ```yaml run: base: nodejs@latest ``` -### os +### os Sets the operating system for the runtime environment. Options and versions are the same as for the build environment. -### ports +### ports Specifies the internal ports on which your application will listen. ```yaml run: @@ -22943,23 +22925,23 @@ run: ... ``` Available parameters: -#### port +#### port Defines the port number on which your application listens. Must be between *10* and *65435*, as ports outside this range are reserved for internal Zerops systems. -#### protocol +#### protocol Specifies the network protocol to use: - Allowed values: `TCP` *(default)* or `UDP` -#### httpSupport +#### httpSupport Indicates whether the port is running a web server: - Default value: `false` - Set to `true` if a web server is running on the port - Only available with TCP protocol - Used by Zerops for [public access](/features/access) configuration -### prepareCommands +### prepareCommands Customizes the runtime environment by installing additional dependencies or tools. :::note `run.prepareCommands` run in the `/home/zerops` directory. ::: -### initCommands +### initCommands Defines commands to run each time a new runtime container starts or restarts. ```yaml run: @@ -22969,13 +22951,13 @@ run: :::note `run.initCommands` run in the `/var/www` directory. ::: -### start +### start Defines the start command for your application. ```yaml run: start: npm start ``` -### startCommands +### startCommands Defines start commands. Unlike `start`, you can define multiple commands that starts their own processes. ```yaml @@ -22992,11 +22974,11 @@ run: - litestream restore -if-replica-exists -if-db-not-exists -config=litestream.yaml $DB_NAME ``` See [start-commands-example](https://github.com/zeropsio/start-commands-example) -### documentRoot +### documentRoot Customizes the root folder for publicly accessible web server content (available only for webserver runtimes). -### siteConfigPath +### siteConfigPath Sets the custom webserver configuration (available only for webserver runtimes). -### envVariables +### envVariables Defines environment variables for the runtime environment. ```yaml run: @@ -23007,7 +22989,7 @@ Defines environment variables for the runtime environment. DB_USER: db DB_PASS: ${db_password} ``` -### envReplace +### envReplace Automatically replaces environment variable placeholders in your static files with their actual values during deployment. ```yaml run: @@ -23019,11 +23001,11 @@ run: - ./config/ ``` Available parameters: -#### delimiter +#### delimiter Characters that wrap your variable names in placeholders (e.g., `%%` means placeholders look like `%%VARIABLE%%`). - Type: `string` or `array of strings` - Supports multiple delimiters simultaneously -#### target +#### target Files or directories to process for variable replacement. - Type: `string` or `array of strings` - Can be specific files or directories @@ -23056,7 +23038,7 @@ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA... -----END PUBLIC KEY----- ``` The placeholder gets replaced with the actual JWT public key during deployment. -### routing +### routing Configures URL routing, redirects, and HTTP headers (only for Static services). ```yaml run: @@ -23073,15 +23055,15 @@ run: X-Frame-Options: "'DENY'" ``` Available parameters: -#### root +#### root Sets a custom root directory for the service. - Type: `string` -#### cors +#### cors Enables CORS headers for cross-origin requests. - Type: `string` - Sets `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, `Access-Control-Allow-Headers`, and `Access-Control-Expose-Headers` - Special case: `"*"` is automatically converted to `'*'` -#### redirects +#### redirects Defines URL redirects and rewrites. - Type: `array of objects` - Each redirect object supports: @@ -23090,7 +23072,7 @@ Defines URL redirects and rewrites. - **status** - HTTP status code (required for absolute URLs) - **preservePath** - Preserve path after wildcard match - **preserveQuery** - Preserve query parameters -#### headers +#### headers Sets custom HTTP headers for specific paths. - Type: `array of objects` - Each header object supports: @@ -23117,7 +23099,7 @@ run: X-Frame-Options: "'DENY'" Content-Security-Policy: '"default-src ''self''"' ``` -### healthCheck +### healthCheck Defines a health check for your application. ```yaml run: @@ -23140,16 +23122,16 @@ run: execPeriod: 10 ``` Available parameters: -#### httpGet +#### httpGet Configures the health check to request a local URL using a HTTP GET method. - **port** - Defines the port of the HTTP GET request. - **path** - Defines the URL path of the HTTP GET request. - **host** - The health check is triggered from inside of your runtime container so it uses the localhost (127.0.0.1). If you need to add a host to the request header, specify it in the host attribute. - **scheme** - The health check is triggered from inside of your runtime container so no https is required. If your application requires a https request, set scheme: `https`. -#### exec +#### exec Configures the health check to run a local command. - **command** - Defines a local command to be run. The command has access to the same environment variables. A single string is required. If you need to run multiple commands create a shell script or, use a multiline format as in the example above. -#### Common parameters +#### Common parameters The following parameters can be used with either `httpGet` or `exec` health checks: - **failureTimeout** - Time in seconds until container fails after consecutive health check failures (reset by success). - **disconnectTimeout** - Time in seconds until container is disconnected and becomes publicly unavailable. @@ -23158,7 +23140,7 @@ The following parameters can be used with either `httpGet` or `exec` health chec :::tip Health checks continuously monitor your running application, while readiness checks verify if a new deployment is ready to receive traffic. For readiness checks, see the [readinessCheck section](#readinesscheck-). ::: -### crontab +### crontab Defines scheduled commands to run as cron jobs within a service. ```yaml run: diff --git a/apps/docs/static/llms.txt b/apps/docs/static/llms.txt index 92ef39a3b..402547314 100644 --- a/apps/docs/static/llms.txt +++ b/apps/docs/static/llms.txt @@ -90,10 +90,12 @@ - [Features > Backup](https://docs.zerops.io/features/backup.md) - [Features > Build Cache](https://docs.zerops.io/features/build-cache.md) - [Features > Cdn](https://docs.zerops.io/features/cdn.md) +- [Features > Coding Agents](https://docs.zerops.io/features/coding-agents.md) - [Features > Container Vs Vm](https://docs.zerops.io/features/container-vs-vm.md) - [Features > Debug Mode](https://docs.zerops.io/features/debug-mode.md) - [Features > Env Variables](https://docs.zerops.io/features/env-variables.md) - [Features > Infrastructure](https://docs.zerops.io/features/infrastructure.md) +- [Features > Local Remote Development](https://docs.zerops.io/features/local-remote-development.md) - [Features > Pipeline](https://docs.zerops.io/features/pipeline.md) - [Features > Rbac](https://docs.zerops.io/features/rbac.md) - [Features > Scaling](https://docs.zerops.io/features/scaling.md) @@ -138,7 +140,6 @@ - [Guides > Deployment Lifecycle](https://docs.zerops.io/guides/deployment-lifecycle.md) - [Guides > Environment Variables](https://docs.zerops.io/guides/environment-variables.md) - [Guides > Firewall](https://docs.zerops.io/guides/firewall.md) -- [Guides > Local Development](https://docs.zerops.io/guides/local-development.md) - [Guides > Logging](https://docs.zerops.io/guides/logging.md) - [Guides > Metrics](https://docs.zerops.io/guides/metrics.md) - [Guides > Networking](https://docs.zerops.io/guides/networking.md) @@ -148,6 +149,7 @@ - [Guides > Public Access](https://docs.zerops.io/guides/public-access.md) - [Guides > Scaling](https://docs.zerops.io/guides/scaling.md) - [Guides > Smtp](https://docs.zerops.io/guides/smtp.md) +- [Guides > Verify Web Agent Protocol](https://docs.zerops.io/guides/verify-web-agent-protocol.md) - [Guides > Vpn](https://docs.zerops.io/guides/vpn.md) - [Guides > Zerops Yaml Advanced](https://docs.zerops.io/guides/zerops-yaml-advanced.md) - [Help > Contacts](https://docs.zerops.io/help/contacts.md) @@ -321,6 +323,25 @@ - [Ubuntu > How To > Upgrade](https://docs.zerops.io/ubuntu/how-to/upgrade.md) - [Ubuntu > Overview](https://docs.zerops.io/ubuntu/overview.md) - [Valkey > Overview](https://docs.zerops.io/valkey/overview.md) +- [Zcp > Concept > How It Works](https://docs.zerops.io/zcp/concept/how-it-works.md) +- [Zcp > Glossary](https://docs.zerops.io/zcp/glossary.md) +- [Zcp > Overview](https://docs.zerops.io/zcp/overview.md) +- [Zcp > Quickstart](https://docs.zerops.io/zcp/quickstart.md) +- [Zcp > Reference > Agent Workflow](https://docs.zerops.io/zcp/reference/agent-workflow.md) +- [Zcp > Reference > Index](https://docs.zerops.io/zcp/reference/index.md) +- [Zcp > Reference > Mcp Operations](https://docs.zerops.io/zcp/reference/mcp-operations.md) +- [Zcp > Reference > Troubleshooting](https://docs.zerops.io/zcp/reference/troubleshooting.md) +- [Zcp > Security > Production Policy](https://docs.zerops.io/zcp/security/production-policy.md) +- [Zcp > Security > Tokens And Project Access](https://docs.zerops.io/zcp/security/tokens-and-project-access.md) +- [Zcp > Security > Trust Model](https://docs.zerops.io/zcp/security/trust-model.md) +- [Zcp > Setup > Choose Workspace](https://docs.zerops.io/zcp/setup/choose-workspace.md) +- [Zcp > Setup > Hosted Workspace](https://docs.zerops.io/zcp/setup/hosted-workspace.md) +- [Zcp > Setup > Local Agent Bridge](https://docs.zerops.io/zcp/setup/local-agent-bridge.md) +- [Zcp > Workflows > Build And Verify App](https://docs.zerops.io/zcp/workflows/build-and-verify-app.md) +- [Zcp > Workflows > Build With Zcp](https://docs.zerops.io/zcp/workflows/build-with-zcp.md) +- [Zcp > Workflows > Create Or Adopt Services](https://docs.zerops.io/zcp/workflows/create-or-adopt-services.md) +- [Zcp > Workflows > Delivery Handoff](https://docs.zerops.io/zcp/workflows/delivery-handoff.md) +- [Zcp > Workflows > Package Running Service](https://docs.zerops.io/zcp/workflows/package-running-service.md) - [Zerops Yaml > Base List](https://docs.zerops.io/zerops-yaml/base-list.md) - [Zerops Yaml > Cron](https://docs.zerops.io/zerops-yaml/cron.md) - [Zerops Yaml > Specification](https://docs.zerops.io/zerops-yaml/specification.md) \ No newline at end of file diff --git a/zerops-llm-script.ts b/zerops-llm-script.ts index 4eb9254c0..7127f4ea6 100644 --- a/zerops-llm-script.ts +++ b/zerops-llm-script.ts @@ -64,7 +64,9 @@ function cleanMarkdownContent(content: string): string { } } - const lines = processedContent.split('\n') + const lines = processedContent + .split('\n') + .map((line) => line.replace(/[ \t]+$/g, '')) const processedLines: string[] = [] let lastLineWasEmpty = false From da5da53eec8633bdea32b9045336d67fac510113 Mon Sep 17 00:00:00 2001 From: krls2020 Date: Sun, 10 May 2026 10:26:54 +0200 Subject: [PATCH 04/24] Refine ZCP docs flow and setup guidance --- apps/docs/content/features/coding-agents.mdx | 21 ++- .../docs/content/zcp/concept/how-it-works.mdx | 126 ++++++++----- apps/docs/content/zcp/overview.mdx | 104 ++++++----- apps/docs/content/zcp/quickstart.mdx | 56 +++--- .../content/zcp/reference/agent-workflow.mdx | 168 +++++++++++++----- apps/docs/content/zcp/reference/index.mdx | 2 +- .../content/zcp/reference/troubleshooting.mdx | 111 +++++++++--- .../security/tokens-and-project-access.mdx | 17 ++ .../docs/content/zcp/security/trust-model.mdx | 22 ++- .../content/zcp/setup/choose-workspace.mdx | 58 +++--- .../content/zcp/setup/hosted-workspace.mdx | 34 +++- .../content/zcp/setup/local-agent-bridge.mdx | 16 +- .../zcp/workflows/build-and-verify-app.mdx | 22 ++- .../zcp/workflows/delivery-handoff.mdx | 73 ++++++-- .../img/zcp/remote-setup-claude-code.jpg | Bin 0 -> 292725 bytes 15 files changed, 592 insertions(+), 238 deletions(-) create mode 100644 apps/docs/static/img/zcp/remote-setup-claude-code.jpg diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx index 43f1b1037..aa5e86aad 100644 --- a/apps/docs/content/features/coding-agents.mdx +++ b/apps/docs/content/features/coding-agents.mdx @@ -17,7 +17,7 @@ ZCP runs with a project-scoped token that grants operational rights inside that Developers using a coding agent on application work that needs real infrastructure. The developer stays focused on the product outcome, technology choices, acceptance criteria, and judgment. ZCP gives the agent the platform context and operations needed to use or create services, connect the app to infrastructure, deploy, verify, recover from failure, and report back. -The **Include Coding Agent** option bundles the currently supported agent CLI shown in the dashboard. Local setup can connect a compatible local agent client after `zcp init`. +The **Include Coding Agent** option bundles the currently supported agent CLI shown in the dashboard and wires it to ZCP. The agent account remains yours: authenticate it with your own subscription login or API credentials. Local setup can connect a compatible local agent client after `zcp init`. ## What ZCP lets the agent do @@ -57,7 +57,7 @@ Decision: [Choose remote or local setup](/zcp/setup/choose-workspace). First tim Remote setup starts with a `zcp@1` Zerops service. Two dashboard options decide what is bundled into that service: -**Include Coding Agent.** Adds the currently supported bundled agent CLI and preconfigures it to use ZCP running inside the `zcp@1` service. Agent authentication is handled separately by that agent's login method. +**Include Coding Agent.** Adds the currently supported bundled agent CLI and preconfigures it to use ZCP running inside the `zcp@1` service. Agent authentication is handled separately by that agent's login method, subscription, or API key. **Cloud IDE.** Browser-based VS Code (code-server) with 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 → Cloud IDE](/features/local-remote-development#cloud-ide-on-remote-setup). @@ -65,6 +65,8 @@ Without **Include Coding Agent**, the service can still exist, but there is no p Both options are configured when you provision the service: [Set up remote ZCP → Path B](/zcp/setup/hosted-workspace#path-b--toggle-the-zcp-service). +Remote setup is still your workspace. You can install another agent CLI, add private MCP servers or helper processes, edit `CLAUDE.md` or other agent instruction files, and keep team tooling in the service. ZCP supplies the Zerops project API and workflow evidence; it does not lock the service to one agent or one set of tools. + ## 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: @@ -99,7 +101,7 @@ Full operation list and permission semantics: [Advanced operations](/zcp/referen ## Customize remote setup -Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product - install packages, run additional processes alongside the agent, or ship a hardened version with team-standard tools baked in. Patterns: [Customize remote setup](/zcp/setup/hosted-workspace#customize-remote-setup). +Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product - install packages, edit agent instructions, configure additional MCP servers, run helper processes alongside the agent, or ship a hardened version with team-standard tools baked in. Patterns: [Customize remote setup](/zcp/setup/hosted-workspace#customize-remote-setup). Local setup isn't extensible the same way; the binary on your laptop is just ZCP - editor, shell, and dev server stay your tools' job. @@ -111,6 +113,19 @@ When the agent runs in remote setup, it lives in a container, not on your laptop ZCP is not a prompt-to-prototype generator, an isolated code sandbox, or an editor-only cloud development environment. It is the control plane that gives a coding agent project-scoped infrastructure operations on Zerops: services, env vars, deploys, logs, verification, recovery, and delivery handoff. +## How it differs from adjacent tools + +ZCP is closest to the tools that let an agent work on real software over time, but the center of gravity is different: the agent operates one real Zerops project rather than a preview sandbox or editor-only workspace. + +| Category | What it optimizes for | Where ZCP differs | +|---|---|---| +| Prompt-to-prototype tools | Fast artifact generation and visual preview. | ZCP targets a running app on real services, with deploy, logs, persistence, and verification. | +| Code execution sandboxes | Safe isolated execution. | ZCP gives the agent managed services, private networking, deployment, and project state that persist across sessions. | +| AI-enabled cloud IDEs | Editing code in a hosted development environment. | ZCP gives the agent platform operations too: create/use services, wire env vars, deploy, verify, recover, and hand off. | +| Primitive/API collections | Calling many specialized APIs. | ZCP keeps the app and dependencies in one Zerops project, reachable by service names and standard platform mechanics. | + +The claim is not that ZCP replaces those tools. It solves the missing operational layer when the output must become a verified app running on Zerops infrastructure. + ## Where to start Build a task board where tasks stay saved after refresh."] - context["ZCP context
live services, env vars, logs, events,
runtime guidance, available operations"] - target["Project fit
use existing services or create missing ones;
select the app runtime,
not the zcp setup service"] - app["Build the app
code, zerops.yaml, env wiring"] + intent["Product intent
Build a task board for my team."] + state["Project state
services, runtime layout, env vars,
logs, events, current work"] + fit["Service and runtime fit
use existing services or create missing ones;
target the app runtime, not zcp"] + wiring["App wiring
code, zerops.yaml, env references,
managed-service connections"] deploy["Deploy through Zerops"] - verify{"Verify real behavior
endpoint or UI"} - fix["Read evidence
logs, events, checks"] - done["Done
URL, endpoint, or UI proof"] - blocker["Blocker
missing credential, decision,
or repeated failure"] - delivery["Future delivery
direct deploy, git push, or CI handoff"] - - intent --> context --> target - target --> app --> deploy --> verify - verify -->|fixable| fix --> app - verify -->|verified| done --> delivery - verify -->|needs human| blocker + reachability{"Platform reachability
service status, logs, HTTP check"} + behavior{"Requested behavior
endpoint, UI flow, persisted state"} + evidence["Read evidence
build logs, runtime logs,
events, checks"] + proof["Proof
URL, endpoint result,
or UI state"] + blocker["Blocker
missing credential, decision,
unsupported fit, repeated failure"] + delivery["Delivery choice
direct deploy, git push,
or CI/human handoff"] + + intent --> state --> fit --> wiring --> deploy --> reachability + reachability -->|fixable| evidence --> wiring + reachability -->|reachable| behavior + behavior -->|fixable| evidence + behavior -->|verified| proof --> delivery + behavior -->|needs human| blocker + reachability -->|needs human| blocker classDef user fill:#f7fbff,stroke:#2d72d9,stroke-width:1.5px,color:#172033; classDef zcpbox fill:#eef7f1,stroke:#32845a,stroke-width:1.5px,color:#172033; @@ -35,39 +36,82 @@ flowchart TD classDef stop fill:#fff1f2,stroke:#d33f49,stroke-width:1.5px,color:#172033; class intent user; - class context zcpbox; - class target,app,deploy,verify,fix work; - class done,delivery done; + class state zcpbox; + class fit,wiring,deploy,reachability,behavior,evidence work; + class proof,delivery done; class blocker stop; ``` -The important signals are simple: the agent read current project state, named the target runtime, verified the requested behavior, and explained what remains. +The agent should make this path visible while it works, but you should not have to prompt each step. ZCP exists so the operational path is available behind a product request. -## Where ZCP runs +## The lifecycle layers -ZCP can run in two places: +ZCP separates the work into three layers so the agent can tell what kind of step comes next. -| Path | What runs where | What changes for you | -|---|---|---| -| Remote setup | A `zcp@1` service runs the `zcp` binary inside the Zerops project. **Include Coding Agent** adds the bundled agent CLI and preconfigures it to use ZCP; **Cloud IDE** adds browser VS Code. | The work stays inside the project. The agent can use project-private networking and SSHFS mounts for runtime files. | -| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your editor's agent talks to it. | Your checkout and dev server stay local. Managed services are reached over VPN, and `.env` generation bridges project credentials into your local app. | +**Service setup.** Prepare the project shape before app work starts: use existing services when they fit, create missing runtimes or managed dependencies when they do not, record the runtime layout, and establish the app target. This layer is not where app code, `zerops.yaml`, or the first deploy belongs. + +**Development loop.** Change code and configuration, deploy the selected runtime services, verify platform reachability, verify the requested behavior, read logs and events on failure, fix from evidence, and repeat until proof or blocker. -The project-scoped control plane is the same idea in both paths. The filesystem, network path, deploy source, and safety profile are different. See [Choose remote or local setup](/zcp/setup/choose-workspace). +**Delivery handoff.** After a verified running result exists, choose how future changes should ship: keep direct deploys, push to git, or hand off to CI or a human release process. The first functional deploy is direct; delivery setup follows proof. -## What ZCP reads +## 1. Read the project before choosing a plan -ZCP reads live project state instead of relying on a long prompt: +The first useful move is discovery. ZCP reads live project state instead of relying on a long prompt or stale notes: -- services and whether they are runtime or managed dependencies, +- runtime services and managed services, - runtime layout, such as one app runtime, a dev+stage pair, or a local checkout linked to a Zerops runtime, -- service env-var keys and references, -- build/deploy events, runtime logs, and verification results, -- the current work state when a session is interrupted. +- env-var keys and references, +- build/deploy events, runtime logs, and recent verification results, +- current work state when a session resumes after an interruption. + +This is why a prompt can stay short. The agent can inspect what exists instead of asking you to paste the project inventory. + +## 2. Fit the request to services and runtimes + +The agent then decides where the app work belongs. The `zcp` service is the control-plane setup, not the app target. App code deploys to runtime services such as `appdev`, `appstage`, or `app`; managed services such as databases, cache, storage, search, or queues provide dependencies. + +If the right services already exist, the agent should use them. If a requested app needs missing infrastructure, the agent can create it before app work starts. If the choice affects product scope, cost, credentials, or destructive changes, the agent should stop and ask. + +## 3. Wire the app to Zerops + +App work usually spans code and platform wiring. The agent may need to update application files, `zerops.yaml`, service references, env vars, migrations, seeds, or framework configuration so the app runtime can reach its managed services after deploy. + +ZCP guidance matters here because Zerops has its own build, deploy, networking, and env-reference rules. The agent should use those rules instead of importing assumptions from Docker Compose, Kubernetes, or a generic cloud template. + +## 4. Deploy, read evidence, and retry from the cause + +Deploy is part of the work, not a separate instruction. ZCP gives the agent access to the Zerops deploy path and the evidence needed when it fails: build output, runtime logs, project events, service status, and recovery hints. -Recovery starts from live state. If a session gets confused or interrupted, ask the agent to read ZCP status before changing anything else. +A failed build, broken start command, missing env reference, or unreachable route should lead to evidence-driven fixes. Repeating the same deploy without a new cause is not progress; after repeated failure or a missing decision, the useful result is a blocker. + +## 5. Verify behavior, not only reachability + +A successful deploy proves only that the runtime built and started. Platform reachability checks prove the service is running and reachable. They do not prove the product request. + +For a task-board app, the behavior check is not "the page returns 200". The useful proof is the user-facing workflow the prompt asked for: tasks can be created, edited, moved, persisted, or shared according to the requested scope. For an API task, proof might be a JSON response from the endpoint. For a worker task, proof might be a processed job and the resulting stored state. + +## 6. Return proof or a concrete blocker + +A finished ZCP-backed app task should leave one of two things: + +- proof: the changed runtime, URL or endpoint, and the specific behavior that was verified, +- blocker: the evidence read, what was tried, and the credential, decision, unsupported fit, or repeated failure that prevents completion. + +That final proof is the practical difference between "the agent wrote code" and "the app task is done". + +## Remote and local setup + +The same `zcp` binary can run in two places. The project loop stays the same; the work surface changes. + +| Path | What runs where | Practical effect | +|---|---|---| +| Remote setup | A `zcp@1` service runs the `zcp` binary inside the Zerops project. **Include Coding Agent** adds the bundled agent CLI; **Cloud IDE** adds browser VS Code. | Work happens inside the project boundary. The agent can use project-private networking and runtime file mounts. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your local editor or CLI agent talks to it. | App edits, dev server, deploy source, and git credentials stay local. Managed services are reached over Zerops VPN, and `.env` generation bridges project credentials into your local app. | -## What counts as done +Choose the setup based on where you want the agent and filesystem to live: [Choose remote or local setup](/zcp/setup/choose-workspace). -A task is not done when code is written, or when a build succeeds. A ZCP task is done when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the URL or a concrete blocker. +## Where to go deeper -Workflow path: [Build with ZCP](/zcp/workflows/build-with-zcp). Exact terms and runtime layouts: [Workflow terms](/zcp/reference/agent-workflow). +- [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. +- [Workflow terms](/zcp/reference/agent-workflow) - exact runtime layouts, delivery terms, and completion evidence. +- [Troubleshooting](/zcp/reference/troubleshooting) - practical recovery when a run gets stuck. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index d36340490..078d37d45 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -1,75 +1,89 @@ --- title: "Zerops Control Plane for coding agents" -description: "What ZCP gives a coding agent, where it runs, and what a finished Zerops app task should prove." +description: "Why ZCP changes how a coding agent works with a Zerops project, and what you can leave out of the prompt." --- -ZCP is the Zerops control plane setup that lets a coding agent work from real project state instead of a hand-written operations checklist. It gives the agent current project knowledge, guarded project operations, deploy/verify evidence, and Zerops guidance for connecting an app to its infrastructure. +import Image from '/src/components/Image'; -It can run remotely inside a `zcp@1` service with **Include Coding Agent** enabled, or locally as the `zcp` binary initialized beside your CLI/editor agent. In both cases, the developer stays focused on product intent, technology choices, acceptance criteria, and decisions that require human judgment. +ZCP is a project-scoped control plane for AI coding agents over one Zerops project. It gives the agent the current truth about that project, project-scoped tools for operating it, and rules for deciding what should happen next and when the work is done. -## What ZCP does +The practical effect is that the agent can work from the project's real state instead of a guessed checklist. It can identify the right app runtime and managed services, make Zerops changes through project-scoped operations, read evidence when something fails, and finish with proof or a concrete blocker. -ZCP makes four things available to the agent inside a normal Zerops project: +That changes the job you give the agent. You provide the product intent, technical constraints, and quality bar. ZCP provides the platform reality: what exists, where code runs, which dependencies are available, what was deployed, what passed verification, and what the next evidence-backed step should be. -**Project awareness.** The agent can read services, runtime layout, env vars, logs, events, and current work state instead of asking you to describe the project. +## What ZCP gives the agent -**Infrastructure wiring.** The agent can separate the `zcp` setup service, app runtime services, and managed dependencies, then connect the app to the right env vars and service references. +**Project truth.** Services, app runtime layout, managed dependencies, env-var keys and references, logs, events, deploy history, verification state, and current work state. -**Deploy and verify loop.** The agent can prove the app works on the selected runtime, not stop after writing code or seeing a green build. +**Zerops control.** Project-scoped operations for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. -**Delivery choice.** After verified work, the agent can keep deploying directly, push to git, or use external handoff to your CI or a human. +**Workflow.** ZCP combines two work loops: infrastructure for using existing Zerops services or creating missing ones, and development for code, `zerops.yaml`, deploy, verification, and delivery choice. The goal is to keep Zerops work aligned with platform and development best practices while staying behind the product task, not becoming another checklist. -You provide product intent and judgment: what to build, which runtime or stage matters when you care, and what result counts as done. +**Evidence-based completion.** A build or deploy is not the finish line. A completed app task should end with a working URL, endpoint result, UI proof, or a blocker backed by logs, events, and verification evidence. -## A good ZCP session +## What you no longer have to script -A ZCP-backed session should be product-led and evidence-based: +Without ZCP, an app prompt often turns into an operations runbook. With ZCP, you should not need to paste: -- The agent reads the project before changing it. -- It uses existing services when they fit and creates missing infrastructure when they do not. -- It deploys and verifies the requested behavior on the real endpoint or UI. -- It ends with a URL or proof of behavior, or with a blocker that names the missing decision, credential, or repeated failure. +- the service map, runtime target, dev/stage state, or managed-service inventory, +- database credentials, private hostnames, env-var references, or generated connection strings, +- build logs, runtime logs, event timelines, or a guess about why the last deploy failed, +- boilerplate like "deploy it, verify it, check persistence after refresh, and report the URL", +- a recap after the chat loses context; the agent can read current ZCP status. -## What you no longer have to do +**You still own the decisions that need human judgment:** product intent, technology constraints, acceptance criteria, external credentials, repository policy, and approval for destructive actions. -ZCP is valuable because it removes the operational checklist from the prompt. You should not have to: +## Where ZCP runs -- describe every service already in the project, -- decide which env-var references connect the app to managed services, -- copy database credentials, logs, or deploy timelines into chat, -- repeat deploy, verify, log-reading, and URL-reporting instructions on every app task, -- translate a failed build or broken route into a guess before the agent can act, -- choose internal workflow names before describing what you want built. +The **same `zcp` binary** runs in both setups. In [remote setup](/zcp/setup/hosted-workspace), Zerops packages it inside a `zcp@1` service in the project. In [local setup](/zcp/setup/local-agent-bridge), you install the binary on your machine and connect your own editor or CLI agent to the project. The mental model is the same: one project, live state, scoped operations, deploy/verify evidence. The filesystem, network path, deploy source, and safety profile differ. -You still own the product intent, technology constraints, acceptance criteria, credentials that live outside Zerops, and approval for destructive actions. +To start using ZCP, choose one path: add remote setup to a Zerops project, or initialize local setup beside your editor or CLI agent. The [Quickstart](/zcp/quickstart) uses remote setup because it needs no local install. -## Where to start +
+ Remote ZCP setup with the zcp service in Zerops and Claude Code running in browser VS Code +
-| Goal | Page | +## Your agent, credentials, and workspace + +**Agent account.** Remote setup can include a bundled agent CLI, currently Claude Code, already configured to use ZCP. Zerops wires the agent to the project; it does not provide or own your model account. You authenticate the agent with your own subscription login or API credentials. + +**Zerops token.** ZCP itself connects to Zerops through a project-scoped Zerops token. In remote setup, Zerops injects that token into the `zcp@1` service. In local setup, you provide a project-scoped token when you initialize ZCP beside your editor or CLI agent. Token details live in [Tokens and credentials](/zcp/security/tokens-and-project-access). + +**Workspace freedom.** The `zcp@1` service is still a normal project service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and shape the workspace around the bundled setup. ZCP is the project control plane available to the agent; it is not a closed agent product. Details live in [Set up remote ZCP](/zcp/setup/hosted-workspace#bring-your-own-agent-or-tools). + +:::caution Production boundary +Use ZCP in development or staging projects. Production should stay in a separate Zerops project and receive promoted work through your CI or release process; see [Production boundary](/zcp/security/production-policy). +::: + +## Start here + +First-read path: + +| If you want to | Read | |---|---| -| Try ZCP once with no local install | [Quickstart](/zcp/quickstart) | -| Understand the model before setup | [How ZCP works](/zcp/concept/how-it-works) | -| Choose remote vs local | [Choose remote or local setup](/zcp/setup/choose-workspace) | -| Add remote setup | [Set up remote ZCP](/zcp/setup/hosted-workspace) | +| Try the shortest first run | [Quickstart](/zcp/quickstart) | +| Understand what happens behind the prompt | [How ZCP works](/zcp/concept/how-it-works) | +| Choose remote or local setup | [Choose remote or local setup](/zcp/setup/choose-workspace) | +| Start building after setup | [Build with ZCP](/zcp/workflows/build-with-zcp) | + +Specific tasks and reference: + +| If you want to | Read | +|---|---| +| Add remote setup to a project | [Set up remote ZCP](/zcp/setup/hosted-workspace) | | Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | -| Build or change an app | [Build with ZCP](/zcp/workflows/build-with-zcp) | | Decide how finished work ships | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | -| Understand tokens and boundaries | [Trust model](/zcp/security/trust-model) | +| Understand tokens and project boundaries | [Trust model](/zcp/security/trust-model) | | Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | | Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | | Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | ## What stays outside ZCP -ZCP is a control plane for agents, not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, scaling, and service references remain canonical for platform behavior. - -## Names that matter - -- **ZCP** - Zerops Control Plane in its coding-agent setup. -- **Remote setup** - a `zcp@1` service inside the Zerops project. The service runs the `zcp` binary and exposes ZCP to the agent from inside the project. -- **Local setup** - the `zcp` binary installed on your machine and initialized in a project folder with `zcp init`. -- **Include Coding Agent** - the remote setup option that adds the bundled agent CLI and preconfigures it to use ZCP in the service. -- **Cloud IDE** - optional browser-based VS Code in remote setup. -- **`zcp` binary** - the executable. In remote setup it is inside the `zcp@1` service; in local setup you install it and run `zcp init` in a project folder. - -For the full vocabulary, including `zcp`, zCLI, zsc, MCP, runtime layouts, and credential names, see the [Glossary](/zcp/glossary). +ZCP is not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, scaling, and service references remain canonical for platform behavior. diff --git a/apps/docs/content/zcp/quickstart.mdx b/apps/docs/content/zcp/quickstart.mdx index df25088d1..e47716747 100644 --- a/apps/docs/content/zcp/quickstart.mdx +++ b/apps/docs/content/zcp/quickstart.mdx @@ -1,70 +1,56 @@ --- title: "Quickstart" -description: "Open remote setup, ask for a small app, and inspect the verified result." +description: "Run the shortest remote ZCP trial: open the agent workspace, ask for a small app, and inspect the verified result." --- -Remote setup is the fastest first run: no local `zcp` install, no local MCP config, and the bundled agent is already connected to the project. You will open a project with **Include Coding Agent** enabled, give the agent a small app request, and inspect what it verified. +Remote setup is the fastest way to try ZCP because the agent runs inside the Zerops project with project access already configured. You do not install `zcp` locally, wire an agent client, or copy project credentials into a prompt. ## Prerequisites - A Zerops account with permission to create a project. -- A login for the bundled agent shown in the dashboard. +- A login or API authentication for the bundled agent shown in the dashboard. Zerops wires the agent to ZCP; your agent subscription or model credentials stay yours. ## Create a remote setup project -The fastest path is a recipe with the **AI Agent** environment: +The shortest path is a recipe with the **AI Agent** environment: 1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes). -2. Pick a recipe that matches the stack you want to try. +2. Pick a recipe close to the stack you want to try. 3. Select the **AI Agent** environment. 4. Deploy the recipe. -The **AI Agent** environment creates the app services plus a `zcp@1` remote setup with **Include Coding Agent** enabled. Keep **Cloud IDE** enabled too so you can open the browser workspace. If you start from an empty project instead, enable **Add Zerops Control Plane (ZCP) service** during project creation and keep **Include Coding Agent** on; the agent can use existing app services or create the missing ones from your prompt. Full setup path: [Set up remote ZCP](/zcp/setup/hosted-workspace). +That environment creates the app services plus a `zcp@1` remote setup with **Include Coding Agent** enabled. Keep **Cloud IDE** enabled for the first run so you can open the browser workspace. -## Open remote setup +Starting from an empty project also works, but it adds setup choices. Enable **Add Zerops Control Plane (ZCP) service** during project creation, keep **Include Coding Agent** on, then let the agent use existing services or create missing ones from your first app prompt. Full setup path: [Set up remote ZCP](/zcp/setup/hosted-workspace). -When provisioning finishes, open the `zcp` service in the Zerops dashboard. If **Cloud IDE** is enabled, open its workspace URL; a browser editor opens with the agent connected to this project through ZCP. +## Open the agent workspace -Ask for what the app should do. Service setup, deploy, verification, and reporting are part of the ZCP-backed agent contract. +When provisioning finishes, open the `zcp` service in the Zerops dashboard. If **Cloud IDE** is enabled, open its workspace URL. The browser editor opens with the bundled agent connected to this project through ZCP. -In the agent chat: +## Ask for a product outcome -```text -Build a task board where tasks stay saved after refresh. -``` - -Add constraints only when they change the product, runtime layout, or delivery path: +In the agent chat, ask for the app behavior: ```text -Build a task board where tasks stay saved after refresh. Use the existing dev+stage pair if this project has one. +Build a task board for my team. ``` +Do not add "deploy it", "verify it", or "send me the URL" just to make the agent finish the work. With ZCP, those are part of the expected app task. Add constraints only when they change the result or the project target: + ```text -After it works, set up git-push delivery to git@github.com:my-org/task-dashboard.git. +Build a Node.js task board with latest PostgreSQL on dev+stage. ``` -## What you should see +## Inspect the result -A good run should make these points clear: +The final answer should give you a real URL, endpoint, or UI proof. Open the URL and try the core task-board behavior the agent says it verified. -1. The agent read current project state before changing anything. -2. It named the app runtime it will change. The `zcp` service is the ZCP setup, not the app target. -3. It used existing services when they already fit, or created missing services before app work. -4. It changed code and `zerops.yaml` as needed. -5. It deployed through Zerops and read logs or events if something failed. -6. It verified both platform reachability and the requested dashboard behavior. -7. It ended with a URL or a concrete blocker. +If you used the technical prompt above, create a task, refresh the page, and confirm the task is still there. If it disappears, the app is not done; ask the agent to verify that exact behavior again and continue from the project evidence rather than starting over. -Open the URL the agent gives you. If the page loads but the requested behavior is missing, the task is not done; ask the agent to verify that exact behavior again. +If the agent cannot finish, the useful output is a concrete blocker: the missing credential, decision, unsupported runtime choice, or repeated failure it could not recover from. ## Next steps - [How ZCP works](/zcp/concept/how-it-works) - the model behind the run. -- [Build with ZCP](/zcp/workflows/build-with-zcp) - how real app work flows. -- [Choose how finished work ships](/zcp/workflows/delivery-handoff) - direct deploy, git-push, or external handoff. - -## Gotchas - -- **Deploy success is not done.** The app behavior you requested must be verified on the real URL or endpoint. -- **The `zcp` service is not the app.** Runtime services such as `appdev`, `appstage`, or `app` receive app code and deploys. -- **Production stays out of the agent loop.** Use ZCP in dev/staging projects; promote to production through your CI or release process. +- [Choose remote or local setup](/zcp/setup/choose-workspace) - when to use remote setup or your local editor. +- [Build with ZCP](/zcp/workflows/build-with-zcp) - how normal app work proceeds after setup. diff --git a/apps/docs/content/zcp/reference/agent-workflow.mdx b/apps/docs/content/zcp/reference/agent-workflow.mdx index f7c469a04..ac6417212 100644 --- a/apps/docs/content/zcp/reference/agent-workflow.mdx +++ b/apps/docs/content/zcp/reference/agent-workflow.mdx @@ -1,91 +1,161 @@ --- title: "Workflow terms" -description: "Precise ZCP workflow terms: session layers, service setup, app completion, runtime layouts, delivery choices, failure categories, and completion evidence." +description: "Precise ZCP workflow terms: session layers, service setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence." --- -Exact vocabulary for ZCP workflows: runtime layout, verification, delivery, failure state, and completion evidence. Day-to-day app prompts should use outcomes; policies and handoffs can use these names when precision matters. +Exact vocabulary for ZCP workflows. Day-to-day prompts should describe outcomes; reference, audits, policies, and handoffs can use these names when precision matters. ## Session layers +Most workflow mistakes come from confusing these layers: + | Layer | What it is | What changes here | |---|---|---| | ZCP setup | Where ZCP runs: remote `zcp@1` service or local `zcp` binary. | No app code should be deployed to the setup itself. | | Target runtime service | The app runtime: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys. | -| Managed services | Databases, caches, queues, search, storage. | Schema/data operations, never app code deploys. | +| Managed services | Databases, caches, queues, search, storage, mail. | Schema/data operations and credentials, never app code deploys. | The `zcp` service is the control surface, not the app runtime. -## Service setup - -Before code work starts, ZCP should identify the app runtime services and managed dependencies. It should use existing services when they fit, create missing services when the project is empty, and resume from live project state if setup was interrupted. - -The service setup phase stops before app edits begin; the full ZCP work session then continues into code, deploy, verification, and recovery. - -## App work completion contract - -For an app intent, ZCP expects the agent to handle code, config, deploy, verify, and retries behind the prompt. A completed session leaves evidence of: +## Service setup routes + +Before app code work starts, ZCP reads project state and chooses a setup route. The route is an implementation detail in first-read docs, but it is useful in reference and audits. + +```mermaid +flowchart TD + start(["Read project state"]) + mid{"Interrupted setup
to resume?"} + runtimes{"Runtime services
already exist?"} + known{"Request matches
known recipe/stack?"} + resume(["resume"]) + adopt(["adopt"]) + recipe(["recipe"]) + classic(["classic"]) + start --> mid + mid -- yes --> resume + mid -- no --> runtimes + runtimes -- yes --> adopt + runtimes -- no --> known + known -- yes --> recipe + known -- no --> classic +``` + +| Route | Use when | Wrong signal | +|---|---|---| +| `adopt` | Runtime services already exist, including recipe-created projects. | Recreating services that exist, or targeting `zcp` as the app. | +| `recipe` | The project is empty or only has ZCP, and the request matches a known stack recipe. | Deploying an unchanged starter as if it were the requested product. | +| `classic` | The project is empty and needs a custom service plan. | Writing app code before service ownership and runtime scope are known. | +| `resume` | A previous setup was interrupted. | Starting from scratch without acknowledging live services and saved state. | + +Service setup stops when app runtimes and managed dependencies are known. App code, `zerops.yaml`, and the first deploy belong to the develop flow. + +## Develop flow + +The develop loop is the main ZCP work cycle. It closes only when reachability and requested behavior both pass. + +```mermaid +flowchart TD + scope["1. Name runtime scope"] + change["2. Change code/config"] + deploy["3. Deploy in-scope runtime"] + reach{"4. Runtime reachability
passes?"} + behavior{"5. Requested behavior
passes?"} + done(["Done: proof or URL"]) + fix["6. Categorize failure,
read evidence, fix"] + blocker(["Blocker"]) + scope --> change --> deploy --> reach + reach -- yes --> behavior + reach -- no --> fix + behavior -- yes --> done + behavior -- no --> fix + fix --> deploy + fix --> blocker +``` + +1. **Name runtime scope.** State which runtime is in scope (`appdev`, `appstage`, `app`, or linked local target). In dev+stage projects, dev work does not imply stage unless requested. +2. **Change code/config.** Edit app files, `zerops.yaml`, env references, migrations, seeds, or framework config. +3. **Deploy directly first.** The first verified runtime deploy uses the direct ZCP deploy path. Delivery setup is applied after a verified running result exists. +4. **Verify runtime reachability.** Service status, recent error logs, and HTTP probe for eligible runtime services. +5. **Verify requested behavior.** Endpoint body, UI state, job result, persisted data, or another check tied to the user request. +6. **Fix from evidence.** Read classification, logs, events, and check output. Repeating the same deploy without new evidence is not progress. + +Dynamic dev runtimes may need an explicit start or restart after deploy. Built-in webserver runtimes do not need a separate dev-server step unless the framework requires one. -- runtime scope, -- app code and `zerops.yaml` changes, -- a direct deploy for the first verified runtime, -- platform reachability, -- requested behavior on the real endpoint or UI, -- evidence-based retries when something failed, -- a URL or blocker. +## Runtime layouts -## Verification layers +Runtime layout describes which app runtime services ZCP should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. -| Layer | What it proves | -|---|---| -| Runtime reachability | Service status, recent error logs, and HTTP probe for eligible runtime services. | -| Requested behavior | The user-requested endpoint, UI state, job result, database state, or stage URL works. | +| Layout | Meaning | Typical names | +|---|---|---| +| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage is explicit. | `appdev` + `appstage` | +| `dev` | One mutable development runtime. | `appdev` | +| `simple` | One runtime with no dev/stage split. | `app` | +| `local-stage` | Local checkout linked to one Zerops runtime for deploys. | local checkout + `appstage` | +| `local-only` | Local checkout with no linked deploy target yet. | local checkout | -Both layers must pass before a session reports completion. +The stage hostname is supplied by project state. ZCP should not invent it from the dev hostname. -## Runtime layouts +## Delivery modes -| Layout | Meaning | -|---|---| -| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage must be named when included. | -| `dev` | One mutable development runtime. | -| `simple` | One runtime with no dev/stage split. | -| `local-stage` | Local checkout linked to one Zerops runtime for deploys. | -| `local-only` | Local checkout with no linked deploy target yet. | +Delivery mode applies after a verified deploy and describes how future changes ship. It does not redirect the first successful deploy. -Runtime layout describes which app runtime services ZCP should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. - -## Delivery choice - -Delivery choice applies after a verified deploy and describes future changes. +| Exact mode | User-facing choice | Meaning | +|---|---|---| +| `auto` | Keep direct deploy | ZCP keeps deploying future changes directly to the target runtime. | +| `git-push` | Push to git | The agent commits and pushes to a configured remote; any resulting build still needs observation and verification. | +| `manual` | External handoff | CI, release process, or a human owns future delivery; ZCP records evidence but does not initiate the next deploy. | -| Choice | Meaning | -|---|---| -| Direct deploy | ZCP keeps deploying changes directly to the target runtime. | -| Git push | The agent commits and pushes to a configured remote; any resulting build still needs verification. | -| External handoff | Your CI, release process, or a human owns future delivery. | +Git-push capability, delivery mode, and build integration are separate. A project can have git-push configured while still using direct deploy for a given session. ## Failure categories | Category | Meaning | Read first | |---|---|---| | `build` | Build phase failed. | Build logs and build commands. | -| `start` | Build passed, runtime failed to start or crashed. | Runtime/prepare logs and start command. | +| `start` | Build passed, runtime failed to start or crashed. | Prepare/runtime logs and start command. | | `verify` | Runtime exists but reachability or behavior failed. | Failing check detail and runtime logs at request time. | | `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and network path. | | `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Structured rejection detail and config files. | | `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | | `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +Categorization is what turns retries into evidence-driven fixes. Recovery moves and symptom mapping live in [Troubleshooting](/zcp/reference/troubleshooting). + ## Completion evidence -A well-run session can answer: +A completed app task can answer: - which services were used or created, -- which runtime scope was changed, +- which runtime scope changed, - which deploy passed, -- which reachability and behavior checks passed, -- which URL or endpoint proves the result, -- which delivery contract applies next, +- which reachability check passed, +- which user-requested behavior passed, +- which URL, endpoint, UI state, or stored result proves it, +- which delivery mode applies next, - or which blocker remains and what evidence supports it. +A green build with a broken route is not completion. A clear blocker is acceptable completion only when it names the failure category, evidence read, fixes tried, and human decision or credential still needed. + +## Practical rules + +- Use existing services when they fit; do not recreate them just to get a clean slate. +- Bootstrap/service setup is not app work; app files and first deploy happen in develop. +- First functional deploy is direct; git/CI/handoff comes after proof. +- Managed services are dependencies, not deploy targets. +- Stage is explicit in `standard` projects; dev changes do not silently promote. +- Runtime health and requested behavior are separate verification gates. +- Stop on repeated failure without new evidence, missing credentials, ambiguous runtime scope, or destructive recovery. + +## Auditing a session + +A session is well-shaped if platform evidence and the agent report answer: + +| Question | Evidence | +|---|---| +| Which setup route and runtime layout were used? | ZCP status, service metadata, project services. | +| Where did service setup end and app work begin? | Work-session state, deploy history, file changes. | +| Which runtime was deployed and verified? | Service events, deploy result, verify output. | +| What behavior proved success? | Endpoint/UI/job/data proof in the final report. | +| What controls future delivery? | Delivery mode, git-push state, build integration, or manual handoff note. | + Operation-name reference lives in [Advanced operations](/zcp/reference/mcp-operations). Recovery shortcuts live in [Troubleshooting](/zcp/reference/troubleshooting). diff --git a/apps/docs/content/zcp/reference/index.mdx b/apps/docs/content/zcp/reference/index.mdx index 8ded40b38..41afbc850 100644 --- a/apps/docs/content/zcp/reference/index.mdx +++ b/apps/docs/content/zcp/reference/index.mdx @@ -7,7 +7,7 @@ Exact terms, operation names, and recovery detail live here. Day-to-day app work | Need | Page | |---|---| -| Name the workflow phases, runtime layouts, verification layers, and delivery choices | [Workflow terms](/zcp/reference/agent-workflow) | +| Name service setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence | [Workflow terms](/zcp/reference/agent-workflow) | | Look up common MCP operation names for agent-client policy or debugging | [Advanced operations](/zcp/reference/mcp-operations) | | Turn a running service into a re-importable bundle | [Package a running service](/zcp/workflows/package-running-service) | | Recover a stuck or confused session | [Troubleshooting](/zcp/reference/troubleshooting) | diff --git a/apps/docs/content/zcp/reference/troubleshooting.mdx b/apps/docs/content/zcp/reference/troubleshooting.mdx index 2134aab48..d6877805e 100644 --- a/apps/docs/content/zcp/reference/troubleshooting.mdx +++ b/apps/docs/content/zcp/reference/troubleshooting.mdx @@ -1,6 +1,6 @@ --- title: "Troubleshooting" -description: "Fast recovery for ZCP sessions: read status first, use failure categories, and follow the right evidence." +description: "Recover ZCP sessions by reading current state, failure category, logs, events, token state, VPN state, and local setup artifacts." --- Start recovery from current project state, not from chat memory. @@ -9,38 +9,107 @@ Start recovery from current project state, not from chat memory. Read ZCP status and tell me where this project stands before changing anything. ``` -That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. +That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. ZCP can rebuild the project picture from live services, recent deploys, logs, events, and saved work-session state. -## Failure categories +## Start from the failure category -| Category | Meaning | First move | +When a deploy or verify step fails, ZCP should surface a category with the likely cause and next move. Categories are recovery-shaped. `network` does not mean "the network is broken"; it means the first useful move is connectivity, VPN, SSH, DNS, or public-route evidence rather than editing app code. + +| Category | Meaning | Read first | Recovery move | +|---|---|---|---| +| `build` | Build failed before runtime start. | Build logs and `zerops.yaml` build steps. | Fix build commands, dependency manifests, lock files, deploy files, or build resources. Runtime logs will not explain this failure. | +| `start` | Build passed, runtime failed to start or crashed. | Prepare logs or runtime logs, whichever the failure names. | Check `run.start`, ports, env references, `prepareCommands`, and whether the process binds `0.0.0.0` rather than `127.0.0.1`. | +| `verify` | Runtime exists, but reachability or requested behavior failed. | The failing check, HTTP response, and runtime logs at request time. | Treat it as app behavior or route recovery, not deploy success. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | The transport error and the named network path. | Check VPN for local setup, service status for remote setup, SSH transfer state, DNS/subdomain readiness, or platform timeout. | +| `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Structured rejection detail, field path, or API metadata. | Fix the named setup block, env reference, `deployFiles`, service type, or prerequisite. | +| `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | The credential surface named in the error. | Replace that credential only: `ZCP_API_KEY`, `GIT_TOKEN`, local git auth, `ZEROPS_TOKEN`, SSH key, or app secret. | +| `other` | No known category matched. | Raw reason, service events, and logs for the named phase. | Stop if the same unknown reason repeats; report the raw reason and recent evidence. | + +Build logs and runtime logs are different streams. A build failure shows in the build container; a runtime crash shows in runtime logs. If the agent reads the wrong stream, the diagnosis will be wrong even when the log command succeeded. + +## Deploy and start symptoms + +| Symptom | Likely category | Next move | |---|---|---| -| `build` | Build failed before a runtime could start. | Read build logs and `zerops.yaml` build steps. | -| `start` | Build passed, runtime failed to start or crashed. | Read runtime/prepare logs and check start command, ports, and env vars. | -| `verify` | Runtime exists, but reachability or requested behavior failed. | Read the failing check, HTTP response, and runtime logs at request time. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Check the named network path and, for local setup, VPN. | -| `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Read the structured rejection and fix the named field. | -| `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | Fix the named credential; do not rotate unrelated tokens. | -| `other` | No known category matched. | Read raw events/logs and stop if the same unknown failure repeats. | +| Build exits with "command not found", missing module, npm 404, or compile error | `build` | Read `buildCommands`, manifests, lock files, package manager, and deploy file list. | +| Build or SSH transfer ends with `signal: killed` | `build` / `network` | Treat it as memory pressure first: scale build/source resources or reduce the heavy step. | +| Runtime crashes immediately with `EADDRINUSE`, missing module, or missing env var | `start` | Read runtime logs scoped to the failed start and check the start command/env contract. | +| Database connection refused during init/start | `start` / `network` | Confirm the managed service is running and env references such as `${db_*}` resolve. | +| Preflight refuses `INVALID_ZEROPS_YML` or setup mismatch | `config` | Read the field-level rejection and fix that setup entry; setup names are not always hostnames. | +| Git push returns authentication failed | `credential` | Remote setup needs `GIT_TOKEN`; local setup uses your local git credentials. | -## Common symptoms +Useful prompt: -| Symptom | Likely move | -|---|---| -| Build log shows missing command/module/lock file | Fix build commands, manifests, deploy files, or dependencies. | -| Runtime is `READY_TO_DEPLOY` or restarts continuously | Read runtime logs; the start command or env contract is usually wrong. | -| Public URL returns 502 or connection error | Check port binding, `0.0.0.0`, subdomain readiness, and whether the service is HTTP-eligible. | -| Deploy succeeded but feature is broken | Treat it as behavior verification failure, not deploy success. | -| Local app cannot reach `db`, `redis`, or storage | Bring VPN up and regenerate `.env` if project env changed. | -| Agent targets `zcp` as the app | Correct it to the runtime service. | -| Agent loops on the same failure | Ask for category, evidence read, fixes attempted, and the next human decision. | +```text +Show me the failure category, build logs, runtime logs, and recent events for the last failed deploy of appdev. +``` + +## Verify and public URL symptoms + +A deploy can be green and still fail verification. Verify has two layers: platform reachability, then the requested behavior. + +| Symptom | Likely cause | Next move | +|---|---|---| +| Service is `RUNNING`, but the route returns 500 or 404 | App route, dependency, or request handling is broken. | Read runtime logs at request time and verify the requested route/body, not only `/`. | +| Public URL returns connection error right after first deploy | Subdomain route is still propagating, or the runtime is not bound correctly. | Wait 30-60 seconds and retry; if it persists, check port binding and public-access eligibility. | +| 502 persists after deploy and wait | Start command binds `127.0.0.1`, wrong port, or HTTP server not listening. | Check `zerops.yaml` `run.ports[]` and make the app bind the same port on `0.0.0.0`. | +| Subdomain stays disabled | Worker or non-HTTP service, or explicit public access missing. | Workers do not get useful public URLs. If it is an HTTP runtime, ask the agent to enable subdomain access and verify it. | +| Requested feature fails after a successful deploy | Behavior verification failed. | Continue the loop from logs/events/check output; do not report the task done. | Public subdomain access is enabled on first deploy for eligible HTTP runtimes. Workers and non-HTTP services do not get a useful public URL by design. +## Token and credential symptoms + +Token problems usually happen at startup. The fix is to replace the token on the surface ZCP reads, then restart the agent so the new process inherits it. + +| Symptom | Likely cause | Next move | +|---|---|---| +| `Token accesses N projects; use project-scoped token` | Multi-project or account-wide token. | Generate a token scoped to exactly one project and replace `ZCP_API_KEY`. | +| `Token has no project access` | Token authenticates but reaches no project. | Grant project access or generate a new project-scoped token. | +| `No authentication found: set ZCP_API_KEY or log in with zcli` | No token reached ZCP. | Add `ZCP_API_KEY` under `.mcp.json` `env` for local setup, or check remote service env. | +| API replies with 401 or `AUTH_TOKEN_EXPIRED` | Token expired or was revoked. | Generate a new project-scoped token, replace it, restart the agent. | +| GitHub Actions deploy fails on auth | `ZEROPS_TOKEN` missing/wrong. | Set a Zerops API token as the repository secret named `ZEROPS_TOKEN`. It is not a GitHub token. | + +Full credential model: [Tokens and credentials](/zcp/security/tokens-and-project-access). + +## Local setup symptoms + +These apply to local setup. Remote setup is already on the project-private network and does not need VPN from your laptop. + +| Symptom | Likely cause | Next move | +|---|---|---| +| Local app cannot reach `db`, `redis`, `cache`, or storage hostname | VPN is down or dropped after sleep/network change. | Run `zcli vpn up ` again. | +| `no route to host` or connection refused for managed-service hostname | Same network path issue. | Bring VPN up, then retry. If network works, regenerate `.env`. | +| Local app reads stale project credentials | `.env` is a snapshot. | Ask ZCP to regenerate the `.env` after project env changes. | +| Re-running `zcp init` made ZCP disappear from the agent | `.mcp.json` was rewritten without `ZCP_API_KEY`. | Re-add the `env` block and restart the agent from the project root. | +| Agent does not list the `zerops` MCP server | Agent launched from the wrong directory or config not loaded. | Quit and relaunch from the directory containing `.mcp.json`. | +| `zcp` is not found after install | Install path is not on `PATH`. | Add `~/.local/bin` or the install target to your shell `PATH`, then restart the shell/agent. | + +## Session drift and manual takeover + +Use status when the chat-side picture drifts from reality: + +```text +Read ZCP status, list the services in scope, and summarize the last deploy and verify result before changing anything. +``` + +That is the right move when the agent says it has no context, a browser workspace was reopened, a CLI session restarted, or you are taking over from a previous run. + +For manual takeover, read evidence in this order: + +| Evidence | Why it matters | +|---|---| +| Service-scoped events | Shows deploys, build/start transitions, failed process reasons, scaling, lifecycle actions. | +| Build logs and runtime logs | Separates build failures from runtime crashes and request-time app errors. | +| Verify output | Shows whether reachability and requested behavior actually passed. | +| Git history | Matters when delivery uses git-push; shows what source change corresponds to the deployed result. | + ## When to stop Stop the loop when the agent cannot make progress with new evidence, needs a missing credential, is about to delete or replace a service, or the target runtime/stage is ambiguous. +A repeated retry with the same evidence is not recovery. Ask the agent for the category, evidence read, fixes attempted, and the next human decision. + Before destructive recovery, read service-scoped events, logs, deploy/verify result, and git history when delivery uses git-push. ## Related 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 2b1ebc627..ecc7d79d6 100644 --- a/apps/docs/content/zcp/security/tokens-and-project-access.mdx +++ b/apps/docs/content/zcp/security/tokens-and-project-access.mdx @@ -32,6 +32,15 @@ ZCP validates the token at startup and refuses wrong shapes before the agent can The fix is always the same: generate a per-project token and replace `ZCP_API_KEY` on the surface ZCP reads from. In remote setup, that value is injected into the `zcp` service environment. In local setup, it is the `env` block in `.mcp.json`. +Common startup/auth messages: + +| Message | Meaning | +|---|---| +| `Token accesses N projects; use project-scoped token` | The token can see more than one project. Replace it with a single-project token. | +| `Token has no project access` | The token authenticates but reaches no project. Grant project access or replace it. | +| `No authentication found: set ZCP_API_KEY or log in with zcli` | No usable token reached the `zcp` process. | +| `AUTH_TOKEN_EXPIRED` or HTTP 401 | The token expired, was revoked, or is otherwise invalid. Replace it and restart the agent. | + ## Where the token lives - remote vs local {#where-the-token-lives--remote-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: @@ -75,6 +84,14 @@ Three names, three jobs — keeping them straight prevents most credential confu `ZEROPS_TOKEN` is a **Zerops API token, not a GitHub token** — an 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. Delivery setup should place it in the CI secret store for the repository that runs the deploy. +With GitHub CLI, the secret shape is: + +```bash +gh secret set ZEROPS_TOKEN -b "$ZCP_API_KEY" +``` + +Use the GitHub UI instead if your team does not allow local CLI secret writes. + ## Rotation Rotate in the Zerops dashboard, then propagate to the consuming surface: diff --git a/apps/docs/content/zcp/security/trust-model.mdx b/apps/docs/content/zcp/security/trust-model.mdx index 6a3992004..645771f92 100644 --- a/apps/docs/content/zcp/security/trust-model.mdx +++ b/apps/docs/content/zcp/security/trust-model.mdx @@ -65,9 +65,29 @@ Zerops records platform-side evidence: service events, deploy/build results, run When taking over from an agent, read evidence in this order: service-scoped events, runtime logs, deploy/verify result, then git history when delivery uses git-push. +| Surface | What it proves | What it does not prove | +|---|---|---| +| Service events | Deploy/build lifecycle, failures, restarts, scaling, public-access changes. | The exact source edit that caused them. | +| Build logs | Build commands, dependency install, compile/package failures. | Runtime request failures after deploy. | +| Runtime logs | Start crashes, port binding, request-time app errors. | Why the build failed. | +| Verify output | Whether platform reachability and requested behavior passed. | That unrelated app flows work. | +| Git history | Source changes pushed during git-push delivery. | Shell edits or uncommitted work that never reached git. | + +Filter events and logs by service hostname when possible. Project-level timelines include unrelated services and older failures that can mislead an audit. + ## What ZCP refuses by design -ZCP refuses boundaries that should never be inferred: tokens that do not resolve to exactly one project, remote self-deletion, and destructive replacement before the agent has read the relevant failure evidence. Destructive confirmation details live in [Tokens and credentials](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions). +ZCP refuses boundaries that should never be inferred. + +| Refusal | Trigger | Recovery | +|---|---|---| +| Token resolves to multiple projects | Account-wide or multi-project token. | Use a token scoped to exactly one project. | +| Token resolves to no project | Token authenticates but has no project access. | Grant project access or generate a new project-scoped token. | +| Remote setup self-deletion | Agent tries to delete the `zcp` service it is running in. | Do it manually from outside that service if you really intend to remove ZCP. | +| Service deletion without named approval | Destructive delete requested without explicit same-conversation approval and service name. | Ask the human for explicit approval naming the service. | +| Destructive replacement before diagnosis | Import/override would replace a service with failed deploy history before logs/events were read. | Read the failure evidence first, then ask for confirmation with the evidence summary. | + +Destructive confirmation details live in [Tokens and credentials](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions). ## Next steps diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index ed2d4575a..e898a6380 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -3,7 +3,11 @@ title: "Choose remote or local setup" description: "Choose remote setup inside Zerops or local setup in your own editor/CLI." --- -Choose where the ZCP-backed agent runs. ZCP gives the agent the same project-scoped Zerops toolset in both paths, but the filesystem, network path, deploy source, and safety profile are different. +Choose where the ZCP-backed agent runs. The same `zcp` binary gives the agent the same project-scoped Zerops operations in both paths, but the filesystem, network path, deploy source, and safety profile are different. + +:::info Public preview +Remote setup is the recommended first run. Local setup is real and supported, but the binary install, `.mcp.json`, and `zcp init` artifacts are still settling; expect setup details to move between releases. +::: Use remote setup for the first run and project-contained isolation. Use local setup when your checkout, editor, dev server, and git credentials should stay on your machine. @@ -13,38 +17,50 @@ Use remote setup for the first run and project-contained isolation. Use local se **Local setup** fits when you want to keep your editor, shell, checkout, local dev server, and git credentials. -## Compare the paths - -**Remote setup** +## Remote vs local at a glance + +| | Remote setup | Local setup | +|---|---|---| +| Where `zcp` runs | Inside the Zerops project in a `zcp@1` service | On your machine as the `zcp` binary | +| Where the agent runs | In the `zcp` service when **Include Coding Agent** is enabled | In your local editor or CLI agent | +| Filesystem the agent sees | Runtime files when mounted into the remote setup | Your local working directory | +| Private service access | Project-private network, no laptop VPN | `zcli vpn up` from your machine | +| Deploy source | Project service / mounted runtime source | Your local checkout | +| Git credentials | Credentials configured in the `zcp` service | Your local git credentials | +| Local install | None for the first run | `zcp` binary, zCLI, project token, agent client config | +| Safety profile | Project-contained by design | ZCP is project-scoped, but the agent client runs as your user | +| Best first use | Yes | After you know you want local-agent control | +| Maturity | Public preview | Public preview, more WIP | -- ZCP runs inside the Zerops project in a `zcp@1` service. -- The agent runs inside that service when **Include Coding Agent** is enabled. -- Runtime files can be mounted through SSHFS. -- Managed services are reached over the project-private network. -- Deploy source, env vars, and git credentials live inside the project service. -- Safety profile: project-contained by design. +## What changes in practice -**Local setup** +In remote setup, Zerops packages the `zcp` binary inside a `zcp@1` service in the project. Enable **Include Coding Agent** when you want the service to add the bundled agent CLI and preconfigure it to use ZCP. Enable **Cloud IDE** when you also want browser-based VS Code. Runtime files can be mounted into the service, and managed services are reachable over the project-private network. -- ZCP runs on your laptop as the `zcp` binary. -- The agent runs in your local editor or CLI after `zcp init`. -- App work happens in your local working directory. -- Managed services are reached from your app and shell through Zerops VPN. -- Deploy source and git credentials are your local working directory and local git setup. -- Safety profile: ZCP is project-scoped, but the agent client runs as your user. +In local setup, you install the same `zcp` binary on your machine and initialize it in a project folder. Your local agent client talks to ZCP from that folder. ZCP talks to the Zerops API directly, while your local app and shell need `zcli vpn up` to reach managed services by hostname. Full install flow lives in [Set up local ZCP](/zcp/setup/local-agent-bridge). -## What changes in practice +Remote setup costs one small `zcp` service in the project, billed like any other Zerops service. Local setup costs local installation and a VPN path, plus a local `.env` snapshot with real project credentials that must stay out of git. -In remote setup, a `zcp@1` service runs the `zcp` binary inside the Zerops project. Enable **Include Coding Agent** when you want the service to add the bundled agent CLI and preconfigure it to use ZCP. Enable **Cloud IDE** when you also want browser-based VS Code. Runtime files can be mounted into the service, and managed services are reachable over the project-private network. +## Runtime layouts -In local setup, the agent works in your local checkout. You install the `zcp` binary and run `zcp init` in the project folder so the local agent can talk to ZCP. ZCP talks to the Zerops API directly, while your local app and shell need `zcli vpn up` to reach managed services by hostname. +After setup, ZCP reads whether the project has one runtime, a dev+stage pair, or a local checkout linked to a runtime. You usually state the outcome, not the label: use the dev+stage pair, deploy to a named stage runtime, or use the single app runtime. -After setup, ZCP reads whether the project has one runtime, a dev+stage pair, or a local checkout linked to a runtime. You usually state the outcome, not the label: dev+stage, one dev runtime, or local deploys to a named runtime. Exact labels live in [Workflow terms](/zcp/reference/agent-workflow#runtime-layouts). +| Layout | What it means | Typical names | +|---|---|---| +| `standard` | Dev runtime plus explicit stage runtime. | `appdev` + `appstage` | +| `dev` | One mutable dev runtime. | `appdev` | +| `simple` | One runtime with no dev/stage split. | `app` | +| `local-stage` | Local checkout linked to one Zerops runtime as the deploy target. | local checkout + `appstage` | +| `local-only` | Local checkout with no linked runtime yet. | local checkout | Stage is a review or promotion target, not production. Production should live in a separate Zerops project; see [Production boundary](/zcp/security/production-policy). +The stage hostname is explicit. ZCP should use the confirmed project state, not invent `appstage` from `appdev`. + +Runtime layout is ZCP's topology term. Zerops service scaling mode, such as `HA` or `NON_HA`, is an individual service setting and is unrelated to this table. + ## Next steps - [Set up remote ZCP](/zcp/setup/hosted-workspace) - [Set up local ZCP](/zcp/setup/local-agent-bridge) - [Build with ZCP](/zcp/workflows/build-with-zcp) +- [Trust model](/zcp/security/trust-model) diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index 900604143..9edea213d 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -5,7 +5,7 @@ description: "Add a zcp@1 service to a Zerops project and choose whether it incl Remote setup is a `zcp@1` service inside your Zerops project. It keeps ZCP, project-scoped credentials, private-network access, and optional agent tooling inside the project boundary. -Enable **Include Coding Agent** when you want Zerops to add the bundled agent CLI and preconfigure it to use ZCP in that service. Enable **Cloud IDE** when you also want browser-based VS Code for watching or intervening. +Enable **Include Coding Agent** when you want Zerops to add the bundled agent CLI and preconfigure it to use ZCP in that service. The agent authentication is still yours: sign in with the agent's subscription login or provide its API credentials. Enable **Cloud IDE** when you also want browser-based VS Code for watching or intervening. After provisioning, the service is ready for your first app instruction. If **Cloud IDE** is enabled, you can open the browser workspace from the dashboard; otherwise use the access method configured for the service. @@ -18,7 +18,7 @@ Zerops provisions the service for you - no local install, no MCP config to write - **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 a `zcp@1` service with **Include Coding Agent** enabled. - **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 base result: a `zcp@1` service inside the project with `ZCP_API_KEY` injected automatically and scoped to this project. **Include Coding Agent** adds the preconfigured agent. **Cloud IDE** adds the browser editor. +Both paths produce the same base result: a `zcp@1` service inside the project with `ZCP_API_KEY` injected automatically and scoped to this project. **Include Coding Agent** adds the preconfigured agent CLI. **Cloud IDE** adds the browser editor. Your model subscription, API key, or agent login is not a Zerops credential; it belongs to the agent CLI you choose to run. ## Path A — recipe + AI Agent environment @@ -58,7 +58,7 @@ If **Cloud IDE** is disabled, remote setup can still exist as a project-containe ## Run the agent -When **Include Coding Agent** is enabled, the bundled agent starts already connected to this project through ZCP. In the Cloud IDE path, the terminal has the bundled agent available with `ZCP_API_KEY` in its environment. +When **Include Coding Agent** is enabled, the bundled agent CLI is installed and already wired to this project through ZCP. In the Cloud IDE path, the terminal has the bundled agent available with `ZCP_API_KEY` in its environment. You still authenticate the agent itself with that agent's own login flow, subscription, or API key. For a connection sanity check, ask the bundled agent: @@ -76,7 +76,21 @@ Build a task board where tasks stay saved after refresh. Discovery, service setup, deploy, verification, and final reporting happen behind that app request. -If **Include Coding Agent** is disabled, the `zcp@1` service exists but no preconfigured agent is waiting inside it. Enable the option or use [local ZCP](/zcp/setup/local-agent-bridge) from your own agent client. +If **Include Coding Agent** is disabled, the `zcp@1` service exists but no preconfigured agent is waiting inside it. You can enable the option, install your own agent CLI in the service, or use [local ZCP](/zcp/setup/local-agent-bridge) from your own agent client. + +## Bring your own agent or tools + +Remote setup is open by design. Zerops provides the project-scoped ZCP connection; the agent runtime around it is yours to shape. + +You can: + +- authenticate the bundled agent with your own subscription login or API credentials, +- install another agent CLI in the `zcp` service and configure it to use the same ZCP project connection, +- add other MCP servers or helper processes for private APIs, internal tools, design systems, or repositories, +- edit agent instructions such as `CLAUDE.md` for project-specific policy and working conventions, +- keep extra dotfiles, shell config, package managers, linters, or framework tools in the service. + +ZCP is the Zerops project API available to the agent. It does not own the model account, replace the agent client, or prevent you from adding your own tooling. After the first setup pass, runtime services can be SSHFS-mounted into the service filesystem with one folder per runtime. 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 remote setup. @@ -92,7 +106,17 @@ At this point, the agent can receive the first app instruction. The [Quickstart] ## Customize remote setup -Remote setup is a normal Zerops service, so teams can customize it like one: add tools, preload team config, or run helper processes next to the agent and Cloud IDE. Keep this separate from app runtime work; deploy app code to runtime services, not to `zcp`. +Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated by **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product. + +Common patterns: + +- **Add team tools.** Install missing packages in the `zcp` service. To make them persistent, move the install commands into the service `run.initCommands`. +- **Preload developer config.** Put dotfiles, shell config, framework bootstrap, or onboarding scripts into init commands so every workspace starts the same way. +- **Run helper processes.** Add side processes next to the agent and Cloud IDE: cache warmers, inspectors, internal CLIs, or your own local helpers. +- **Fork the base image.** When init commands become too large, build a hardened image based on `zcp@1` with team-standard tools baked in. +- **Add another MCP/helper server.** The service can run project-local helpers alongside ZCP when your team needs a private integration. + +Keep this separate from app runtime work. Deploy app code to runtime services, not to `zcp`. ## Gotchas diff --git a/apps/docs/content/zcp/setup/local-agent-bridge.mdx b/apps/docs/content/zcp/setup/local-agent-bridge.mdx index 29a984043..d44913b18 100644 --- a/apps/docs/content/zcp/setup/local-agent-bridge.mdx +++ b/apps/docs/content/zcp/setup/local-agent-bridge.mdx @@ -7,7 +7,9 @@ Local setup runs the `zcp` binary on your laptop, initialized in one project dir `zcp init` writes the local MCP configuration for that folder, so a compatible local agent client can talk to ZCP as the `zerops` MCP server. With `zcli vpn up `, your laptop also joins the project's private network - the same network your services use. -Local setup is newer than remote setup. Expect install details and generated project files to move between releases. +:::warning Public preview +Local setup is the part most likely to change. The binary install path, `.mcp.json` shape, and files written by `zcp init` are still settling between releases. +::: Use this path when you already have a comfortable local setup - your editor, your shell, your dev server - and want a coding agent that can drive Zerops from the same machine. If you're evaluating ZCP for the first time, [remote setup](/zcp/setup/hosted-workspace) is a faster start. @@ -42,7 +44,17 @@ In a terminal in your project's working directory: zcp init ``` -`zcp init` writes the local MCP configuration and agent instructions for this project. Re-running it may refresh generated config; after rerunning it, confirm `.mcp.json` still contains `ZCP_API_KEY` before launching the agent. +`zcp init` writes the local MCP configuration and agent instructions for this project. + +Current generated artifacts: + +- `.mcp.json` - MCP configuration your local agent client reads to discover ZCP. +- `CLAUDE.md` - agent instructions for operating ZCP in this project. +- `.claude/settings.local.json` - Claude Code per-project settings when that client is used. +- `~/.config/zerops/aliases` plus a shell-rc sourcing line - helper aliases for launching the agent from this project. +- `.zcp/state/` - appears later, on the first workflow/adoption/session state write. + +Re-running `zcp init` may refresh generated config. `CLAUDE.md` preserves edits outside managed markers, but `.mcp.json` is rewritten from the token-less template. After rerunning it, confirm `.mcp.json` still contains `ZCP_API_KEY` before launching the agent. ## Configure `zcp` as the local MCP server 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 a074f62b8..3d7cdb6b5 100644 --- a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx +++ b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx @@ -7,6 +7,8 @@ After the target runtime and dependencies are known, the agent can change code a A ZCP task is done only when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the working URL or a concrete blocker. +Localhost is not delivery. A local dev server can speed up implementation, but the completion proof comes from the Zerops runtime or the linked deploy target. + ## What the agent does ZCP treats deploy as diagnostics and verification as the completion gate. The user does not have to script this sequence; it is the expected path behind an app prompt. @@ -30,6 +32,16 @@ If the agent starts writing files before it knows the runtime scope, treat that The first deploy uses the direct ZCP deploy path. Delivery choices such as git-push or external handoff apply after a verified deploy exists. +A direct deploy waits for the Zerops build/deploy process to finish and for the runtime to be ready enough to verify. Git-push delivery is different: the push returns first, then the agent observes the build integration or events that follow and verifies afterward. See [Choose how finished work ships](/zcp/workflows/delivery-handoff). + +Build and runtime evidence are separate: + +- Build runs in a temporary build container. Build logs explain compile errors, missing packages, lock-file problems, and failed build commands. +- Runtime logs explain start crashes, port binding, request-time 500s, missing env vars, and app-level failures. +- Events show deploy/build transitions, service lifecycle, scaling, public access, and failed process reasons. + +Remote setup can deploy multiple hosted runtime services in parallel when the task spans them. Local setup deploys from your working directory to the linked runtime target one service at a time. + Deploy failures point to different evidence: | Failure point | Read first | @@ -54,7 +66,15 @@ Both layers must pass before the agent reports completion. The agent should name the failure point in plain language, read fresh logs/events/check output, fix the indicated cause, and redeploy. A repeated retry with no new evidence is not progress. -Stop and ask for a decision when scope is ambiguous, credentials are missing, destructive recovery is needed, or several evidence-based retries hit the same failure. +Use this cadence as the expected shape: + +| Attempt | What should change | +|---|---| +| 1-2 | Diagnose from the named logs/events/check output and fix the targeted issue. | +| 3-4 | Re-check `zerops.yaml`, env vars, `deployFiles`, build commands, start command, and health/public route assumptions. | +| 5 | Stop and ask. Five attempts without progress means the loop needs a missing decision, credential, or human diagnosis. | + +Stop sooner when scope is ambiguous, credentials are missing, destructive recovery is needed, or several evidence-based retries hit the same failure. ## Review and take over diff --git a/apps/docs/content/zcp/workflows/delivery-handoff.mdx b/apps/docs/content/zcp/workflows/delivery-handoff.mdx index 6bdfe09a6..f0560f441 100644 --- a/apps/docs/content/zcp/workflows/delivery-handoff.mdx +++ b/apps/docs/content/zcp/workflows/delivery-handoff.mdx @@ -3,15 +3,19 @@ title: "Choose how finished work ships" description: "After a verified deploy, choose whether future changes use direct deploy, git-push, or an external handoff." --- -Delivery is decided after the app works. You can include delivery intent in the original product prompt, but ZCP applies it only after a verified deploy exists. The first deploy uses ZCP's direct deploy path; the delivery choice records how future changes should be delivered. +Delivery is decided after the app works. You can include delivery intent in the original product prompt, but ZCP applies it only after a verified deploy exists. The first deploy uses ZCP's direct deploy path; the delivery choice records how future changes should ship. -## Three delivery choices +If you do not have a CI or repository-driven release process yet, keep direct deploy for now. Git-push and external handoff become useful when source control, review, or a separate release owner matters. -**Keep direct deploy** for fast iteration, solo projects, and demos. The agent keeps deploying through ZCP directly. +ZCP is for development and staging projects. Production should receive promoted work through your CI or release process; see [Production boundary](/zcp/security/production-policy). -**Push to git** when you want commits, reviews, or a repository-driven build. The agent commits and pushes to a configured remote; a build integration may run afterward. +## Three delivery choices -**External handoff** when your CI, release process, or a human owns delivery. ZCP records what happened but does not initiate future deploys. +| Choice | Exact mode | Use when | What ZCP does next | +|---|---|---|---| +| Keep direct deploy | `auto` | Fast iteration, solo projects, demos, agent-owned dev/stage work. | Future changes keep deploying through ZCP directly. | +| Push to git | `git-push` | You want commits, review, repository history, or repo-driven builds. | The agent commits and pushes to a configured remote, then observes/verifies the resulting build when one is tracked. | +| External handoff | `manual` | CI, release process, or a human owns delivery from here. | ZCP records evidence but does not initiate the next deploy on its own. | You can ask in plain language: @@ -27,23 +31,66 @@ Keep ZCP deploying directly for now. My CI takes over after this verified deploy. ``` -## Push to git +## Direct deploy + +Direct deploy is the path ZCP already used to produce the first verified result. No extra setup is needed. + +Pick this when the project is still exploratory, the agent's deploys are the canonical dev/stage path, or you do not need repository-driven delivery yet. + +Direct deploy is not a shortcut around verification. The next session still needs deploy plus reachability plus requested behavior before it reports completion. -Git-push needs committed code, a remote URL, and credentials that can push. +## Git-push delivery + +Git-push needs committed code, a remote URL, and credentials that can push. It records where future source changes should go; it does not make the first functional deploy happen through git. Remote and local setup differ: -- Remote setup uses credentials configured inside the `zcp` service, commonly a fine-grained git token. -- Local setup uses your local git credentials and working directory. +- Remote setup uses credentials configured inside the `zcp` service, commonly a fine-grained `GIT_TOKEN`. +- Local setup uses your local git working directory and git credential helper. + +Local setup does not initialize git for you. If the directory is not a git repository yet, initialize it and make a first commit before asking for git-push delivery. + +Remote setup can prepare repository state during service setup, but git-push still needs at least one commit covering the work, a remote URL, and push credentials. + +## What fires after a push + +Git-push capability and build integration are separate. A push may trigger one of three outcomes: + +| Outcome | Meaning | +|---|---| +| Nothing tracked by ZCP | Your repository may still have its own hooks or CI, but ZCP did not set up or observe one. | +| Zerops dashboard build integration | Zerops pulls from the repository and runs the build/deploy pipeline from dashboard integration. | +| GitHub Actions workflow | A workflow checks out the repo and deploys to Zerops with `zcli`. | + +The GitHub Actions path needs a Zerops API token stored as a repository secret named `ZEROPS_TOKEN`. It is not a GitHub token. The project-scoped token already used by `ZCP_API_KEY` can be reused if your team wants one rotation surface. A push is not proof that the app works. If a webhook or GitHub Actions build runs from the push, the agent should observe the build result and verify the app afterward. +## External handoff + +External handoff means a human or external system owns delivery after the verified ZCP result. ZCP can still read state, verify, and record deploy evidence when asked, but it does not auto-initiate future deploys as the close condition. + +Pick this when the next step is a release review, a non-ZCP CI pipeline, a production promotion process, or a human decision that should not be hidden inside the agent loop. + +In this mode, ending the session is deliberate: the agent reports what is verified, what is handed off, and what the external owner must do next. + ## Change later -Delivery choice is not permanent. You can move from direct deploy to git-push later, add a build integration later, or switch to external handoff for a release that needs human control. +Delivery choice is not permanent. You can move from direct deploy to git-push later, add a dashboard or GitHub Actions build integration later, or switch to manual handoff for a release that needs human control. + +Git-push capability can also coexist with direct deploy. A configured remote does not force every future task to close through git. ## Gotchas -1. **Delivery choice is not a hidden deploy button.** It records the future path after the app already works. -2. **External handoff is not a deploy by itself.** It means a human or external system owns delivery. -3. **GitHub Actions uses a Zerops token.** The Actions secret for `zcli` deploys is `ZEROPS_TOKEN`, not your GitHub token. +1. **First deploy is always direct.** Setting up git-push early does not redirect the deploy that creates the first verified running result. +2. **Delivery choice is not a hidden deploy button.** It records the future path after the app already works. +3. **External handoff is not a deploy by itself.** It means a human or external system owns the next delivery action. +4. **GitHub Actions uses `ZEROPS_TOKEN`.** That secret is a Zerops API token, not your GitHub PAT. +5. **"No tracked build integration" does not mean no build will fire.** It means ZCP did not set one up or observe it. + +## Next steps + +- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) +- [Tokens and credentials](/zcp/security/tokens-and-project-access) +- [GitHub integration](/references/github-integration) +- [CI/CD with Zerops](/guides/ci-cd) diff --git a/apps/docs/static/img/zcp/remote-setup-claude-code.jpg b/apps/docs/static/img/zcp/remote-setup-claude-code.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3b6adf75f1e9a2a8c565bc200c7de7a221a72d06 GIT binary patch literal 292725 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

msLGV$BczO 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

9OQU zQ74l*klgXsXHi10DL0{Miyh_py_Lb9GM_&^9yRh~0Gr?R8&nN*W`vSVwxU1>hL9vp z;4sRkF@NUx^&VxjIGgMQ(8yvXQH?OKW9@BzY9o0t19m`-)!E@%n|bB4_=uz&H?WjL ziP@*n;M%-6%j;ivZNF7y_cmFaZija(=UDcO-mOPLF~zLpnad5s zA3~vQNp;Mw6IHZMshWIqR(R}XI`>3<{mHr&570VwfbZR=*dMfP3S}Jp}SSVSeHX z4OX}}o}==WbW*c>R`FHFq{SqUE|wt@dL`*B#aKa5CpX;R%g*+a8U%5LrevIy!e~F! zE^vnMA>B8Q<|AnAplE8;MH#A4@3<}2$t5a)AW8j}X$#HP_mX-vZ`Z4@+Zc9>3-iuo z0h4E7t!vLXhv2o6T1_4M^_f4|nnpXYworpYj}ZcS6xJbVw9skeTUAaiU?+=+H94rh zYbQy=|J;+)=I2@2YmzMHt@7f&D_&>0APx_+JHYLgsF2W6#9M?cxU63nq3+4hZs}m9 z{v$HbYqn+q1^l6IlL5Fso2d)`Ms%F3UqfEhfQgLO;g@~I-uz1h-1ym{xyy{ z;_=5$WM*AnJQx0c)vZg@P)tnj`hT{{D;%}w)KbF^J|yX%L_c0N(n4pe7<0}S#v0%B zM+^UZ4&Kyxn_yy6aJtma`K|2$!CD|KB=L0A1wk*=Ge&r#vY2*=R!FDau+ z9KXjz{*cj!hoFKjlc>T@Q1l$I4+gsoj2&d8 zpEAVyfNF^gK_7wV^`rgcv=4|bN^ZAX!bJ7Tz%Q@W!*`i)p)8$>6xjQ& zO=25)(KeCR&5Vy^R(R!~$Zbib`s=Pci;9vvTXcK6)*m`kL_1m+N9#~Behrz8+YA@$ zTRNr+kb|zvI~K1o70sCRuys5#B)}6Y+34qvnM1(HZ!4mx+pXXLIdMtmOG6iB846oW zAJR%8hXeQ?mOVh0cPQxcg|bBxCDpx>Pi`e*6-JHgCUgU{+#jr|SfknC>hy6~D%lb= zQ>PJ)xz0@eOdA@X#7+z3?L#DW7k1UpLa_8vrvH$&D0=}O<>{~heZV+bEAR-(obD+f zjud#2rCw~I$CYv`oHfd?fV>?DnyV7j!YiZIf zw0DSUGD43BE8-Up#s-Wxv`LClM!MT-N>}S`nVoN`q=M;jKsU=>X1Y9PJs*&OD87Jk z?4bI#s*??t>9g(-^?LzmYu$?H5w!gT@B>`Z6;OaTKh8?B9CI#0?o`f*kps?43nFn1bE(Q6_l;>h)M)&@a%s`o`_(bfle1<59i9{r zcH`Iga7U#}*-GGd)p~A}v@K8h7!#KQ=q43*yXQ?Aar9H!x=NP565E33lj8uLca3o} zVbJZGqnHUy3Ou(F>1f-*Z967}t|&dW)3Zo>G>=tCEcKO#vcz{c6;Snc{9?Z5XT>WjDAp%efs zk}+G=8Mc!6Hf|)1Oh5G@krgKYK(Tc?u8mT^;4ZF!xYJMG9a3KF$3P`#P|kVFnj{hT zx9|T6<@u#5)V%QA{|qSpN!p5XPxJ}~9g=8LX7Jf+AJ~^+Kh%5qnZ(*^bpyoXwRZP# zxF0L`|67QLO<|g86J}`Zq3p?U-iAi$&%~`d;<|ZUSA$|;kpsdQ&dab$oCaCz?5&OkD`GVRqdZjBfP>zbDFRkF0K{i> zE1=@Z;bh9BPYp>Rf`T&j)|pmTQlKlQQ})}Jl#T=v

HTHtf$>=AN~IcKPZm>b{bd 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℘ z56_qGs_KS@Zs^&pB`9(SQzz~_W-8%l7~g_)b=5$DD85=ZURBUo4oEJjPWyq z#iluU1hJ7gHG&7c`3IMEW6QnL;dHiwnXb;RNdjvsCC{@r8O6Q;lz0gS9>y(y6$KF1 zX+)B%UR{nSd?)p`&6Bet2-~gm(x3Y$03Q~Kf;akEL@08Qq}MoF{wu|-`()BwFRCI+ zR`h+2MHV6|;!SNnCT9V0Pwyw{k5dmy`XTlnPNra#*uuuBN7(luCtZS7sgwh=)e-ow zE?x(9A&3^&86u{mUReMb(QixmN7COnr z4+;yAEd>g=6pmZFIIe%TPRg>th@x^x2Xqjr_hU7fYY`z5yjCr`x>?s}?Z!`D+zxl1GUKXu-WfBTnm$ zi=W`It}Kf!DrP^N%I+%$%jM&E&}9hPzz{J(KuhZdQ%x~kxjGlA`WFCGl_v1uz*pvZt2DISUieINi zk8H0i@;;1uTeiqJ(AjL^@>p_SnlkLb+t5W~*rK_hn7#7pw~`8s+Kwl053ZG?DDsYW zZl!jiB7SQ1zwBg$s<8Ca+D+*~qx*qW`!shFxcG9!r4tNqQ{1gf5~dWIE=f*$5s#d6 zspBk3F}=nz>NRR^ERY3QrA(1ip^z%GnK2?1Eb-G-kufhFdbau=W7th&u|uGg8y5Ap zBe|+DljYSYmtHvdwFu=Q?M&$IGq^T?d>bz0BG_6F1vp|aOLJT=1h{!lQdpZg^70&m zX>}X;^giHYNYJT(ish+rYceV#?Bv)A0%;UA3Io2}J+EI~NDu?bFsdoCThuL+ycRDNs=;-swr|h1^8!Z2nE3s zh{z>kWULuxto290As85FXQsRRl{faRc2U+y!~`0%0K_>e>RBQ`ytLM0c3y+&LujHjUjX7uL-IR^OpY z49+rqS?B68gGSHhyc$Z&UEwF#ezICVI6hLM|;H{aej^4#$?> z7~6IZ4hq6-bYGKA(+$hF@dSGis1TCqlS-=x7_iyxXH{9hrBXRGp=msL( z9ul?&c%MgXe-r*@&p#6ipj+dHf8e7HIoU_A<99?w_mSGG{jnV_+Gy<)H#pMw$+8GW z@sj#}3=865IEddp8-4=hFMgmbELB+K+XTU=#0vUVH3EhOEb1ESO9WhkEGey6P>E^E=>i2B6#$-1lrwCCo@u|@3 z_||Ru(m?e zg8=NlA0MJSY}gGtb6~ zyh*JFC5}uh)iQ>)f8*M#ihH%SzpFy4vjZI<^8VjYl;8+^i$bw6-JS2

?G1D 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 MPAz#6b2al{0C#J)`v3p{ literal 0 HcmV?d00001 From 8e7d203ee9b1c5d4da24aaf7389f391ae3892b2c Mon Sep 17 00:00:00 2001 From: krls2020 Date: Sun, 10 May 2026 12:00:04 +0200 Subject: [PATCH 05/24] Refine ZCP first-run guide flow --- .../docs/content/zcp/concept/how-it-works.mdx | 12 +-- apps/docs/content/zcp/quickstart.mdx | 81 +++++++++++++----- .../content/zcp/setup/choose-workspace.mdx | 33 ++++--- .../content/zcp/workflows/build-with-zcp.mdx | 67 +++++++++++++-- .../zcp/quickstart-cloud-ide-claude-code.jpg | Bin 0 -> 128634 bytes .../quickstart-laravel-ai-agent-recipe.png | Bin 0 -> 266203 bytes .../img/zcp/quickstart-zcp-service-ready.png | Bin 0 -> 37871 bytes 7 files changed, 150 insertions(+), 43 deletions(-) create mode 100644 apps/docs/static/img/zcp/quickstart-cloud-ide-claude-code.jpg create mode 100644 apps/docs/static/img/zcp/quickstart-laravel-ai-agent-recipe.png create mode 100644 apps/docs/static/img/zcp/quickstart-zcp-service-ready.png diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx index b9eb481fe..2e18d85b5 100644 --- a/apps/docs/content/zcp/concept/how-it-works.mdx +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -44,15 +44,15 @@ flowchart TD The agent should make this path visible while it works, but you should not have to prompt each step. ZCP exists so the operational path is available behind a product request. -## The lifecycle layers +## The three parts -ZCP separates the work into three layers so the agent can tell what kind of step comes next. +ZCP separates an app task into three parts. These are not checklist items you run by hand; they are the shape of the work the agent should make visible while it operates the project. -**Service setup.** Prepare the project shape before app work starts: use existing services when they fit, create missing runtimes or managed dependencies when they do not, record the runtime layout, and establish the app target. This layer is not where app code, `zerops.yaml`, or the first deploy belongs. +**Infrastructure setup.** Establish where the app should run and what it depends on. The agent should use existing services when they fit, create missing runtimes or managed dependencies when they do not, and confirm the app target before writing code. This part is not where app code, `zerops.yaml`, or the first deploy belongs. -**Development loop.** Change code and configuration, deploy the selected runtime services, verify platform reachability, verify the requested behavior, read logs and events on failure, fix from evidence, and repeat until proof or blocker. +**Development and verification.** Change code and configuration, deploy the selected runtime services, verify platform reachability, verify the requested behavior, read logs and events on failure, fix from evidence, and repeat until proof or blocker. -**Delivery handoff.** After a verified running result exists, choose how future changes should ship: keep direct deploys, push to git, or hand off to CI or a human release process. The first functional deploy is direct; delivery setup follows proof. +**Delivery after proof.** After a verified running result exists, choose how future changes should ship: keep direct deploys, push to git, or hand off to CI or a human release process. The first functional deploy is direct; delivery setup follows proof. ## 1. Read the project before choosing a plan @@ -72,6 +72,8 @@ The agent then decides where the app work belongs. The `zcp` service is the cont If the right services already exist, the agent should use them. If a requested app needs missing infrastructure, the agent can create it before app work starts. If the choice affects product scope, cost, credentials, or destructive changes, the agent should stop and ask. +The important user-facing distinction is simple: infrastructure setup prepares the project shape, while the development loop proves the app behavior. If the agent starts deploying before it knows the runtime target, or keeps creating services after the app is already running, ask it to read project state and explain the target. + ## 3. Wire the app to Zerops App work usually spans code and platform wiring. The agent may need to update application files, `zerops.yaml`, service references, env vars, migrations, seeds, or framework configuration so the app runtime can reach its managed services after deploy. diff --git a/apps/docs/content/zcp/quickstart.mdx b/apps/docs/content/zcp/quickstart.mdx index e47716747..4b95544a6 100644 --- a/apps/docs/content/zcp/quickstart.mdx +++ b/apps/docs/content/zcp/quickstart.mdx @@ -1,47 +1,86 @@ --- title: "Quickstart" -description: "Run the shortest remote ZCP trial: open the agent workspace, ask for a small app, and inspect the verified result." +description: "The fastest practical way to try ZCP: deploy an AI Agent recipe, authenticate Claude Code, open Browser VS Code, and ask for an app change." --- -Remote setup is the fastest way to try ZCP because the agent runs inside the Zerops project with project access already configured. You do not install `zcp` locally, wire an agent client, or copy project credentials into a prompt. +import Image from '/src/components/Image'; + +This is the fastest way to try ZCP and understand it in practice. You deploy a ready-made recipe, authenticate the bundled agent, open the browser workspace, and ask for an app change in natural language. + +Zerops has a [recipes catalog](https://app.zerops.io/recipes) covering many runtimes and frameworks. Recipes can ship multiple environments for the same stack; the **AI Agent** environment is the ready-to-go ZCP setup. It creates app services plus the `zcp@1` workspace service, browser VS Code, and bundled agent wiring in one deploy. + +This quickstart uses the [Laravel showcase recipe](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent) because it includes real managed services and an AI Agent environment. The same path works for other recipes that offer AI Agent. ## Prerequisites - A Zerops account with permission to create a project. -- A login or API authentication for the bundled agent shown in the dashboard. Zerops wires the agent to ZCP; your agent subscription or model credentials stay yours. +- A Claude Code subscription login or API authentication. Claude Code is the bundled agent in the current dashboard flow; support for additional agents is planned. Zerops wires the agent to ZCP, but your agent subscription or model credentials stay yours. -## Create a remote setup project - -The shortest path is a recipe with the **AI Agent** environment: +## 1. Deploy the AI Agent recipe 1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes). -2. Pick a recipe close to the stack you want to try. -3. Select the **AI Agent** environment. -4. Deploy the recipe. +2. Open a recipe with an **AI Agent** environment. For example, open [Laravel showcase](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent). +3. Select **AI Agent**. +4. Keep **Coding Agent** and **Cloud IDE** enabled. +5. Deploy the recipe. + +

+ Laravel showcase recipe with the AI Agent environment selected and ZCP configuration enabled +
+ +The deploy creates a development runtime, a staging runtime, managed dependencies, and a `zcp` service. The `zcp` service is the workspace where the agent operates from; app code still deploys to the app runtimes, not to `zcp`. -That environment creates the app services plus a `zcp@1` remote setup with **Include Coding Agent** enabled. Keep **Cloud IDE** enabled for the first run so you can open the browser workspace. +## 2. Authenticate Claude Code -Starting from an empty project also works, but it adds setup choices. Enable **Add Zerops Control Plane (ZCP) service** during project creation, keep **Include Coding Agent** on, then let the agent use existing services or create missing ones from your first app prompt. Full setup path: [Set up remote ZCP](/zcp/setup/hosted-workspace). +When provisioning finishes, the dashboard opens the agent authentication flow. Use your own Claude Code subscription login or API credentials. Zerops wires the agent to this project through ZCP, but the agent account stays yours. -## Open the agent workspace +If you close the prompt, open the project in the dashboard and then open the `zcp` service. The control-plane panel shows the browser workspace, web terminal, SSH path, desktop IDE path, and agent authorization state. -When provisioning finishes, open the `zcp` service in the Zerops dashboard. If **Cloud IDE** is enabled, open its workspace URL. The browser editor opens with the bundled agent connected to this project through ZCP. +
+ ZCP service panel with Browser VS Code, web terminal, SSH, desktop IDE access, and agent authorized state +
-## Ask for a product outcome +## 3. Open VS Code and Claude Code -In the agent chat, ask for the app behavior: +After authentication, continue into **Browser VS Code**. The workspace opens with the project filesystem, ZCP configuration, and the Claude Code panel available in the editor. + +
+ Browser VS Code workspace with the Claude Code panel open +
+ +## 4. Ask for a product outcome + +In Claude Code, ask for the app behavior in natural language: ```text Build a task board for my team. ``` -Do not add "deploy it", "verify it", or "send me the URL" just to make the agent finish the work. With ZCP, those are part of the expected app task. Add constraints only when they change the result or the project target: +Do not add "deploy it", "verify it", or "send me the URL" just to make the agent finish the work. With ZCP, those are part of the expected app task. Add constraints only when they change the product, stack, runtime target, acceptance criteria, or delivery path: ```text Build a Node.js task board with latest PostgreSQL on dev+stage. ``` -## Inspect the result +## 5. Inspect the result The final answer should give you a real URL, endpoint, or UI proof. Open the URL and try the core task-board behavior the agent says it verified. @@ -51,6 +90,8 @@ If the agent cannot finish, the useful output is a concrete blocker: the missing ## Next steps -- [How ZCP works](/zcp/concept/how-it-works) - the model behind the run. -- [Choose remote or local setup](/zcp/setup/choose-workspace) - when to use remote setup or your local editor. -- [Build with ZCP](/zcp/workflows/build-with-zcp) - how normal app work proceeds after setup. +Read [How ZCP works](/zcp/concept/how-it-works) next if you want the model behind what just happened: project state, runtime fit, app wiring, deploy, verification, proof, and blockers. + +Read [Choose remote or local setup](/zcp/setup/choose-workspace) when you are deciding how to start a real project: AI Agent recipe, new-project toggle, existing project, or local setup beside your own editor. + +Read [Build with ZCP](/zcp/workflows/build-with-zcp) before giving the agent larger tasks. It explains the three practical phases you will use during normal work: infrastructure setup, development and verification, then delivery after proof. diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index e898a6380..655affcfd 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -9,7 +9,7 @@ Choose where the ZCP-backed agent runs. The same `zcp` binary gives the agent th Remote setup is the recommended first run. Local setup is real and supported, but the binary install, `.mcp.json`, and `zcp init` artifacts are still settling; expect setup details to move between releases. ::: -Use remote setup for the first run and project-contained isolation. Use local setup when your checkout, editor, dev server, and git credentials should stay on your machine. +Use remote setup for the first run and project-contained isolation. Use local setup when your checkout, editor, dev server, and git credentials should stay on your machine. Both paths let the agent discover services, wire the app, deploy, verify, and report proof from the same Zerops project. ## Short rule @@ -40,23 +40,34 @@ In local setup, you install the same `zcp` binary on your machine and initialize Remote setup costs one small `zcp` service in the project, billed like any other Zerops service. Local setup costs local installation and a VPN path, plus a local `.env` snapshot with real project credentials that must stay out of git. -## Runtime layouts +## Starting points -After setup, ZCP reads whether the project has one runtime, a dev+stage pair, or a local checkout linked to a runtime. You usually state the outcome, not the label: use the dev+stage pair, deploy to a named stage runtime, or use the single app runtime. +Remote setup has two common entry points: -| Layout | What it means | Typical names | +| Starting point | Use when | What happens | |---|---|---| -| `standard` | Dev runtime plus explicit stage runtime. | `appdev` + `appstage` | -| `dev` | One mutable dev runtime. | `appdev` | -| `simple` | One runtime with no dev/stage split. | `app` | -| `local-stage` | Local checkout linked to one Zerops runtime as the deploy target. | local checkout + `appstage` | -| `local-only` | Local checkout with no linked runtime yet. | local checkout | +| Recipe with **AI Agent** environment | You want the shortest first run or a known stack baseline. | Zerops deploys app services plus `zcp@1`, Coding Agent, and Cloud IDE together. | +| New or existing project with **Add Zerops Control Plane (ZCP) service** | You want a custom project, or a project already has services. | Zerops adds the `zcp` workspace service; the agent then uses existing services or creates missing ones from the app request. | + +Local setup starts from a local checkout. Run `zcp init` in that folder, add a project-scoped token, and connect your local agent client. The project may already have Zerops runtimes, or the agent may need to create or link a runtime before deploys can happen. + +## App runtime layout + +After setup, ZCP reads how app runtimes are arranged. You usually state the outcome, not an internal label: use the dev+stage pair, deploy to a named stage runtime, use the single app runtime, or link this local checkout to a stage target. + +| Shape | What it means | Typical names | +|---|---|---| +| Dev + stage | The agent can iterate on dev, then prove the result on a separate stage runtime. | `appdev` + `appstage` | +| One mutable dev runtime | Fastest project shape for experiments and early builds. | `appdev` | +| One app runtime | A small project with no dev/stage split. | `app` | +| Local checkout + Zerops target | Your local files are the source; a named Zerops runtime receives deploys. | local checkout + `appstage` | +| Local checkout only | ZCP can inspect and generate env snapshots, but deploy needs a linked runtime later. | local checkout | Stage is a review or promotion target, not production. Production should live in a separate Zerops project; see [Production boundary](/zcp/security/production-policy). -The stage hostname is explicit. ZCP should use the confirmed project state, not invent `appstage` from `appdev`. +The stage hostname is explicit. The agent should use confirmed project state, not invent `appstage` from `appdev`. -Runtime layout is ZCP's topology term. Zerops service scaling mode, such as `HA` or `NON_HA`, is an individual service setting and is unrelated to this table. +Exact runtime-layout labels are listed in the [Glossary](/zcp/glossary#project-and-runtime-terms). Zerops service scaling mode, such as `HA` or `NON_HA`, is an individual service setting and is unrelated to this table. ## Next steps diff --git a/apps/docs/content/zcp/workflows/build-with-zcp.mdx b/apps/docs/content/zcp/workflows/build-with-zcp.mdx index 326d0ac07..de036b062 100644 --- a/apps/docs/content/zcp/workflows/build-with-zcp.mdx +++ b/apps/docs/content/zcp/workflows/build-with-zcp.mdx @@ -15,19 +15,72 @@ Do not make the prompt longer to restate completion expectations such as deploy, You also do not need to paste an infrastructure inventory, env wiring plan, or log summary into the prompt. ZCP makes those available to the agent from the live project. -## What happens after your prompt +## The three parts of the work -The agent should make this path visible as it works: +The useful mental model is infrastructure, development, then delivery. ZCP makes those phases explicit enough that the agent can resume, recover, and stop at the right boundary without turning your prompt into an operations script. -**Project discovery.** Read what exists now: runtime services, managed services, env vars, logs, recent events, and current work state. +### 1. Infrastructure setup -**Service setup.** Use existing services when they fit, or create the missing runtime and managed dependencies before app code starts. +The agent first reads what exists now: runtime services, managed services, env vars, logs, recent events, and current work state. -**Runtime scope.** Identify the app runtime that will receive code, such as `appdev`, `appstage`, `app`, or a linked local deploy target. The `zcp` service is never the app target. +Then it establishes the app target: -**Code, deploy, verify, fix.** Change app files and `zerops.yaml`, deploy through Zerops, verify reachability and requested behavior, read evidence on failure, and retry from the cause. +- use existing runtimes and managed services when they fit, +- create missing runtimes or managed dependencies when the request needs them, +- identify the app runtime that will receive code, +- keep `zcp` as the workspace/control-plane service, not the app runtime. -**Delivery after proof.** After a verified deploy exists, keep direct deploy, push to git, or use external handoff to CI or a human. +This phase ends when the app runtime and dependencies are known. It should not end with "the app is done"; no application behavior has been proven yet. + +Useful prompts: + +```text +Build a Node.js API with PostgreSQL. Use the existing dev+stage pair if this project has one. +``` + +```text +Use the existing Laravel services and add a small notes app. +``` + +### 2. Development and verification + +After the target is known, the agent changes app files and `zerops.yaml`, deploys through Zerops, verifies platform reachability, verifies the requested behavior, reads evidence on failure, and retries from the cause. + +Two checks matter: + +| Check | What it answers | +|---|---| +| Reachability | Did the runtime build, start, and answer on the real platform path? | +| Requested behavior | Does the app do the thing you asked for on the real URL, endpoint, worker result, or stored state? | + +A successful build is not enough. A page returning 200 is not enough when the prompt asked for saved tasks. The agent should report the behavior it checked, not only the deploy it ran. + +Correction signals: + +| If the agent... | Ask it to... | +|---|---| +| writes files before naming the runtime target | read ZCP status and identify the app runtime first | +| targets the `zcp` service as the app | switch to the runtime service that runs app code | +| repeats deploys without new evidence | read fresh logs, events, or verify output and name the failure point | +| reports completion after only a green build | verify the requested behavior on the real URL or endpoint | + +### 3. Delivery after proof + +After a verified deploy exists, choose what should happen to future changes: + +| Choice | Use when | +|---|---| +| Keep direct deploy | Fast iteration, demos, early development, or agent-owned dev/stage work. | +| Push to git | You want commits, repository history, review, or repo-driven builds. | +| External handoff | CI, release process, or a human owns delivery from here. | + +You can include this intent in the original prompt, but it applies after proof: + +```text +Build a task board where tasks stay saved after refresh. After it verifies, push the result to git@github.com:my-org/task-dashboard.git. +``` + +The first functional deploy still happens directly through ZCP. Delivery choice controls what follows a verified result. Advanced export/reuse path: [Package a running service](/zcp/workflows/package-running-service). diff --git a/apps/docs/static/img/zcp/quickstart-cloud-ide-claude-code.jpg b/apps/docs/static/img/zcp/quickstart-cloud-ide-claude-code.jpg new file mode 100644 index 0000000000000000000000000000000000000000..038b66f7d23d0877f01543c0b33cb0b45323ccbc GIT binary patch literal 128634 zcmeEv2|SeB|Nm69msGeVv{-J0RH)G07D+-WCVNs@$JhshDJ|Migf5}To|!B&gE6Ko z*+R%RYj$B~tTV%y@gK^)xBI*I`|Ga1d%u6rGq3ZU=XuUK&vQQK{aMcEob${~+sp^R zx|7Fsj{_Dh0stQHcfd>s;HtKd!xaEPUmqY3005Q)1Qu-u2=Zh6B!EMU#D9n%U-T_t z$=vnD06?$b;aW$_L0Du5`ggXWy@E1 zE?Kl4uvmD}65&NNWdJGuTL>V^il3l#9bnNC{auZQOP&ySDY=5s_y`d$Xl4?iPLR-f%R0U(oW7 zv){?sim47qcqE=amUDeX_MFCMrm8k1j8 z_^Pb@O+{r@^}F}YEv;?sgb&?4y?y;32gb%Hm@M|>)NC#J)csURemGap*-8iq{#Z+4 zz~aUHdM;VKL~ya-VgZ5RMSL19S+{th}6BVs|eYOb={=fdR@8~MchX09IU{E8gc;aA1jWVSMwE?=3g zkB6t1_iUZ6UcYhkQ@tV{MSiMU((})i%gN3AT)&di`5HFOSFy9}b0tTAs3j3_lwXJ0 zZ3+J;$ghjwQbGQS(?d&G>!#2Y&WD&mv0Re$!{NK7&D^{)h z&D#tCuzJZH`Goo48iOcJNGWBZ=l7Id)++PyKYDzzB^|d=qUTrl=-oEEJ7pPpvO0_II>)K_);EqZ4mi zxl-LCV=e^r|GMLJYPd(QB2xoBr;!PQNGi)@ha@8tm%4%*MBvAisOjjq-jm`BfFQinQs* zL{6xKM{7mg%pE6Ei&Zj*lv;Z-4!otJm0|kojO{`80OG(Q&dSAh;3~=NsHM1K7ex&A)SJ`uE?rcn(ZBdL~?e8YXre(*7CR3%|Tx%mY%BD;bBG9vWTo0ar3Q)8h zYe6QT8FN8q73==5?DoGiVagznt`LaC~ z_{2I1|HL?JKsBJX_X))EjQ9gYX|Mh~7o@W?Tw9y_#2qPvy~RxyAvdKmKq_ScBClGb z%>35qds*pyG*GCUw);2(c&zYAjr`Vd_4UNxn&sb;e!Oq&-Z*sZ?vG9XINSdXn(#!O zkEa)sX9oNWX_i|0-iYuVEaH`k?#xg8ldIw+=U`{qD?P@z4TL3gxzvq0ZIT zM*&uWl;n$(>|Xjo!bsL9LxW~@;5(v8*+lgSm&VgGHg_a?deuP;#y0=8{QxF)uCJ4w znGvWYid!1}czp91jsHd}7bio0G?6WuzRHaGHQVnmHV)5{+$1XGlKZ`z5j^v8Az5tN zP#|)wZ6v{FgpvTvVOhrq-a;zvodKLh?jD19Pj+JEc+YBDGyGE-!WPj{%qKLMP7qgd zOlEI!&!OuGuS{h-q#y&U9Irsw&#+9I&GZ+y!>3kw(St@%Z`03M7(T`HJ5{+y{9wEM zA?nY!%3s_HW{pUNDK8!2f&g)|4mwh!U(%z}Lk>BCKQJ(L{1BvhhJx~Bek{J&Jjgrzi7)=55C)0%3ybDkfbPUI z)%Bp{$=s_(YVXVM^{RdaD}R-k_^ai~SG~laRhci#p%q&W9I~9p)xQd*Kfd(eAG0F% zP?CCnY_|oteF>{yJNp4{X67J;IhWa=>XF;C_Rg>R;ePIy`*)oq!J~{bqXR_8akV@g z&Nr!7qSm!GK&)=Qqc(3>{lVjZac7L^kDW8%F#+WqeIGl7$NTp#*8J|)#qvMTFe}~K zANzZIaR2appRG$Y@|eC1$}PlumVFj@_p7AM54<~X|1Uu9tH3@Fxp_&w0JjCWeHC!# z`RunH=i!!F9I2@&g7Jyz_?LCt{(dv~$0yDk!~^z`#Sb$pq`Z%(O4>Sr7CIDP+28+6 zzd|dgXQ}Gx#(=jZvs2k?1V?5wErWBzc*pR>=6 z5&bzu{liKDp(mkflU_fz?*iPu%1N~Vx8DWi&v3I1sw-qpXp*75FGn+md>G$#d@Lq> z8X5a@1}^^l$5BjMOj~`=fNEO{jmC7;;VQUjQY&IIa$r^9#PLT5o%q= z^$F(ZT8Il;#IL2G;!ynb%A{Qx&-y^LpXWK{WuEHLUDRuYVe@FdK*lc{Gg#p!`Hmlw07JHH<~p|2RwVhbawz z!1ezBf!iH9H3Qfe!=N27a4e+n)Ln2Mf0ZwfKPPg2;Ah^MwctGdDj(pyH*djt{FOla zM5qP0&GXrBJ1)TOH^FS4D;MDQ+h8`IdI4?=aQiAXI1iXVc3gnl0^AnhHm|h)smBFn z`un8Ze}{C-wm;9_tunDh>NG!46!iXE7Lxq(vW`FC3Hhlvb#-Hb4{H}HDzcPR0FJ6K1zz`FvuO3K&noEk@@9@ zkHweK)3DZ};H49{M?=Cuh)GeRI}JS@yMWVq_W8#h|0_i09~X1KwS<3@Si~fbqI5rX zV=>P`vGhUEDr}a85*_7=EreLOP!gMm8sbelD z3s&(U&kkWRFwc3V+-q)v!FXLOmx6uEAgs;6;;L+ zk69BFTexJJNe)=G#+(_TxNmeVJtm~iPe{cCskIhS)D%6i!Xi4s53|84TaR`sv)|YQrhZi;t0a)56%r)dif*nTo4ZcbPX$L$UG~r&n((;(al&0<3C}w}_HKZs zphm#i#<7D5B#+JUlDQ!L)1_yJDvZm{ITqMg7*E*hTkhQ)V2JZ5_u3CZd2%ri9s4=T z^34s+FPjTPtxd8K%~vwAeyc$FPwD?n!sgcudJF>8`aQ$5Bl8A^d9<}2OZ5*-!+<`j zc6f8`ia|B!iqi*GN24ZWBl({k`5IyI$I|?ZM9r`HoRH7q*WVnp4B!*VzcW1rzKaXL`-E%7O{@yTJM1AQk5!@OK^; z;I;s_zfl%0z-<9;3vl~S=KVk6-hwjyeJXfCx-Cez1?l!rkYNAm9Tue9f^_>`a_T>U z>eufrz-<9;3vl}z)#3u&7T~r3w_jIC|K>Xj%5*`xEl9To>Gp4yVZZ+F3({>tx-Cez z1?l$d65`){XF<9xNVf&)wjka9%`)uQ-+e*4El9To>9!!V#m{bkqI!dXvm1QGS%&UV#JTNu3hu~N4D`SZEK=oo<_fKV^ zpH9e+baU1XRzj0Ky*fdMg6mpW;c}yq8mRGGsGgYC=D{*nl4wyAOk2cLQOzq7)fY{dA%vD%MT9)@T8H#V1L;=Qa~`ra+E0B8YJOX$4{!IX%;jj=07O#(LagrfQx7^a;~{k@zX156%zpl#T6a%)Z+WQ-tZcyFPG)RBsJqzP9z4m}?8 z!zuaKgEB#rb%=@jCj$wFqg>0>-8CQ^r#>?r{KZj{i5GJ2vt=iLlJcNU(5BZQYzc@g^g{?f>(W z*MTwJ6Sb*8UiuMLI`E|at(^$ep&ji;>|Ij7?x-dJ7GX3B?ESTfH#(iWVtpSv_!J)Nj%DxHad0gO)eUR)sqXpP zTk?Ip;fwCUpMEds$M~l<$wkLt4Ty1BNYmbslGBs%V|5CDZw{MTC! zzdhwScT0%OFWcJxCq41^fu#=yzRlkpb}fzl*99Anzkcs5F&ijqm%O+5>w{)ZsCAW* zJ4#OBr$J)UsZTBTKP^66XL&@+!Aw+W2F*4Ddw$?=JCVpdDEjF;96t-^VNIycoE5#< zT%)20zxS=-P6L~X2U7Ta>4~a!DSh~yug`zu?hte5XV3cl0g(TE)f#M}=Dzv(e@ujN zs+zJ^&S=sANxi5s1>7wbZ}LHe0pq);>I^aEUkVeVW5dBjk@SOJFuTwH@7+$@L%H_m ztRnvtShL@pFl)AgiuAzz0(`>*vu&-FAU;D*Jz$p9eb6<=MY=+-<-|mQe^PNlDl&Rb ziT@5$<3XMg^Ov%{?@H^Lx@$ndWUXpY=mig2fsKidgI81q0wX%T2Cek?KhrU>_Z49C zCe3<$YEhhPiysfd77Zj*kiGrZcJM9GW+K9gq7Bkptt@ zJ>%8>O@(8A9h;?_hn|4aL9M|>tbKXqggEo%M;G5V0jr!JG zhUq}0D2{e;VAzxy1bTD^Rxao(lA1zkt*;BHPAoDsNVSUSI9Zc9b zVu=x*^e9R-tzxk?eyEF?tiu7bh!llSASFzH`j1|0=A}up)Vs~YUaiB$ar{4WpZngJ zFUeKJ7-v|c8UQjKi#W+y6*l=!WSkW;Tv&{?8@+{0);ijOv$lLUUH%&p|09QNvrqm- zhM70AAjYFvv*OuO$Gpc<+xy9_<~Cp9>!xGt-=Ujv&`K1P8E=}YX_uL2`*r~5R_i#p zsMax`UW5T9qB>M9@1}yF^+p4OhUEi;1LKqwM%J`3q^V5l$0!d|~NyW*L&ad^$ znNJKzk2Iq#`AxaA`u&qE-T5FbEly-l+{xR*V6w6TjTRX>r!uJWL9&L`0_Z@%ZNj;ps63CeKSc zBjn#dNq?@ZDx_?*HR4G5dreDzuj~zVxd!#{WQ~n zMMY&NhFwoO*$cR?18?-k-Plpx2qB_%wGC?g53=nU-p^SC)US($YjR z@K?%{=qe?KR>EF`-P|+ptr|SWZBuk{ImK*MdvhZBUeW2AgEN35454wg;X@z0W&n!T zr3oXCX8`JZbMh=Fnrriz5kzxCUoBeVkV}zrUOU7*s=mXX!J=VG8puB3r6rszJyOA% za-tI`XGzIil3JvXU#-=~h>|l&B{^)rI-!;DV#B5IHz%6iz`ptPmIvouw33bwF1zLJ zh6A#1YLIim5fEAqxPdMP3Y0B2Z$mPWwj(#9PU30VdNL_BQxIP_2JyYq$np00KGMhx zAPU)0&wdLdm|0j`=uDV*$|PGG$8TTEd@~3x@9ZmoD}J{5=eKAEAU$ z>Kk62b2P`CbUwhQ&8poJ+-lj&A!6>TOAOROHqiGDwWvpcSuiv=pvVdmzXMOD)E1G4 zpt@9K1kQznYnkxnzx}rD+ebue9elGH@rv!YiGoiw1Tpqn^*-(1*b`_)YB^R#+&(wB zlu1UDO9BvdeY`DswTZZB3skWrpm7XcTR=2@6B2tdQg|qK52$lhvN-{&31bIeFnR5k zhKL)*<6CC{y9P?cVG1f<&_W7_+}i}p z*A|>Wl3w1TRM++LrY60yHz$Hv%bymbXmhRS1GAg~fM)Ea4oAC|A+*t#2TV1PfG7YNurd=up6ZAe=(Xw80 z25^1`Ks)-1N&}Xjp}Db5HZkirI-83Qij5yFP0u5J7)JDM8m0!(n*xg1NA0fCTFuQc zxFp9g@bC=a)(d4Ox4slUT8D{`g>4cRq<5f57KXW-U$fNXV*{cHuc6}2xF999v*$Ht zc}tSQf1VQuGX0*;F`RCa9D`Rz`LGJbK_M?%vQkSl)QC{WuqA8*dLz|oIID$tsI;S} z9qj6(#(=P-R*J8Q_8CqYbj(7Ge&bDClL5{R9B$6=){4Er2e#LGgE91j!?$!3Ez-Ec zDDErZN#}Rf`La>EN_LS_iE71^Iq+bD@dO@m2B4}@+{FKLrivn}d#O_)DS~Bl$D<~n z#u7K~BV`j1tZ(4dhFOM|-6?&`u;~m*sOiji(huE{^utA_IyMZ>4zB9P#kykhoVjWd z1L_N`>Cv~_G)?7#rd8bW{jy~XJMDR|>n1>L(ufA>@!d@j58Dgv;BQ-De zb1V@hHEtuk65K~veA9Yl*0fx%i>PRecO#>CRht@&n_J40!WP7qL`Mfoi;MmED84l0 z+=)831CJw%!}ROjve#RXYWC3S-T77%*vd2n{b{^@pLe85E7ZhD7wZxp3(Jf>pu}1= zI0ImfZV2a+Ian5y&LC5SpY!h0Y;s4Iai)KoGgf;do-9-QmKDJ?ZmF4C zD#0oRKBc=bV>QyD$Ys_RY>>MK^4jsUeD7;gq3BsD2!bN9UDq?0(wIS9d8 zrR*Bl&Ndk^Kku)VZCM4+uFs>gBTCgc648`l{LLX&_lYOcLpRbLB{%B7P@`-lveaRA zhp1ju$DCj{+{p3rwWS_>i-Q#k@7m3<#gwJt^|e=tqmpY#YBK;YPA>d_ZN3#a7mn|$ z;Ay{-A7UV~HVBADs~J5zy@HwfPIDVtyAWb^kR)A5U6e$|e2=--g=F4w>PVXbXj9nc z61f+KBhiyf8*JA0z&iD>9g@lqF5Rx6EI>n>gc2czWOW6|N^taEy17?Nv+o2Ht-T8y zH;#?u;>ucb>CjvTHgQ_r1|vZ&i5B;Yug$h#tRyfkL%|h;Qrryfj>n1D{f9{$P57vi z55GCqHY9y7J1M(|NiZxTaI5Cpy4ynFx~=Spk2Br&h*FP25G*9iepuqYH<13T-LTE!T?bKdqQsg<(gOci5H%u70?OedWYkDUi8t%3$Z?uIO^gZi8BI5!vS! zZ>p51UC&418ykYiXfo9{nF@a3eugDG19-TLZ=*c)UZLw9y%R^?rM~=X0oIau^3hEp z(en_|#Xv!AB}OrgL*i|tP6La;;XV-;$jpGGa&eJlbUD7wS)&LWe4PgIH^vVUbCO4Z z!BC=pSQ&3id$S#$NE2_aV|>snhnN?TQ?rW;i7qaz8NgadNz34C>?Kav9VH|*uEKw~ z{`MJIr?@@3eytBeqPUWNNH`Ze0=$h5X+K&XC`EB*=`$R(qo?o1AK+UfrMS2rTzOHf zNSoV`d-B`I5cKGBExrLcSCc5@tciN4Q#MAn;8NskO>|?efJT}0I+#8mXr^=aVg+#I zaRA#citKpC4EyC}xVrlE9lNk;6nu_W(g)6=xaNJnWS$vB;mv6-3=QwHOo;qC1M-$trqkeRj z9=7Z=82O597N)}|G8LokaRo8Gp38grJob|+Nen@SymiTniVdukFX%aE^8fGvo(p7h zeZADABX6!belEqnK4&rY3IrjNM>LSRbzY2h<%^6OZ0NMxDkBk5+LrzVyIO&b@pG2T zJoP1*X&6CWD2CMT9n=ZCOb3v_abIFU1FXK{Zl?#z{OC$_=53cVQzuyoXqwBwXy=IK z&r?EG%5|lr?KBVV^AIVoApex2G>qlpD|rlI3mNk=laC~qr$yFZ-2c-E@&zf^ zHaxA7M#rjV47Y7&TJ)Oqfv;q81Z*f@5FZuI?q%@aTfQsQ*xObxSsSdL_XSRzlt*f~ ziLiV4ck0Ryp{t=9((6f(Z{rmLzoh01e1$rknaBc;EOX#Bz`n$zckVtc8H^Cghh;U9 zE_`odHR$;v^n|z7mqj#T>EBAFK;@?F+~NF#TL9Ek#vy-%yU06@ntd@AZMVBD zGGjJvji7(Y$Iz%2dgT-K5RC0%@yPc^)^sAG)#)<$OVZ(@#@19T?uxc?y?1zR-(|;; zVyY|7a-91!aF|xuZPN&Bv7%0!x(px54{65bk|4>rh%f03lSu3wA;PkXf|#4FcBnz) zs_QJ@%mAp}v@awIl?q9{>y>mRZ)Z0f4*6a@F0!0(CG1Pq_DnyaVAgG1*LjD@?hcoU z6!~Pc&m;d+Fa1~L&L5%bLf$!HSXs9Yb;u$tdPh&?r27w%J&|&}<6r89y=*;hDqN*= z20)$xRM!6B9N|mK z_b6jwOdzFV7*Ht8+$psarl4mJ?7sY^uGyh}6Hc(j5uB0Txi8VgE>1KNobq7^P1MI? z?#%$o@&6-(8X0c^Mst+9Wc!?^-%J+PqEbC5sU*HCd>B2e3g@}MxQ6#cxu|*n5 zBXIf*p!>?t-WqrVPk`RTSNQMX$?(ZF5=vRerQVmip1^-+=nPiw z&a|*6L5tX?{W&Wksez z(ID)x*jv=4R5+Spt`!XQK@o5DVdvv$tUS;rL;mjI5p-z%pSU= zILZ4SF1rn$rzd>Tr6^D>!N=rv2Q5Zu-H;pjc#E)`$J*KtRR|_>jkX((4RZf9954Qa>^xpYo&pqRl|MbC%asZ z1#aLjdI?sGMZdOl`c&w&#AR7>XWVt3ie<$WRIp7!dzSO`0f8gFUG3ANnOVq^;rh;R z)eM*Gdgc09ikZ`jt2$O?hJ=+a$C0}U-g<-hcg1Ve(T7As&5>({T8MdAs|qwPgp5|i zj$2RP3H+(G(tV0Nt)1`82X8SwwrT{z(IL3ddxk?99-UJCw7mFliD+(@!?8o#^2Fl@ z1AMorSV6)Z(c~RDxO1%7wLHUqVvsSG1{?`zpT35*A{sh*d?qQCk95TA%M$bs0o~&b zbc}l72GYgw`ce}qnlO8`@gp=|kpRKO;y0v@yOehj^~`AlZ7vOrSigr{t>NHcsFNk9 z#E+d@;yrfW#Y$qhj$u~}j#i;aE#-IPe8gBB#c;)G6&!4;Xp>?HrQ?HMJ^Pg=2yF#v zJltVFgmH4i>J)<$MiQK*M((oD1x6939lSQP6z{eWSkMrSJn^2e>Gv8!D-dRtd7L%)P)!{Ht1SezSZsm+4lId%gAL^!!+&8I z%j$c1xJD~BzwJliusN2^6Fs9@7|Xia_4@mA^or7pH8);6;E3{_Yo>;(F5^x{ zfQuuy69bOjCbI0CRpTdIvl@49BS_uerJO?U%%NO3=dDquXQrvWBXkW~#jCYo5q+2LT#oWx8md1Cjg%eeIg5l*q`%G24|QO;(67Y$k`Wj04)h9?dU?2P zEi6E(#onB_9^s1rUX&QZjagG${S-x37vET;IaVV(fvtW-7weDLh<%(mOyia2yxx~% zs9-WtbRujO#O*v|Cq@07Is2$*ElR^UcOu;zTN`U%Kw%hnh$}p1N(t5Aq*gjdnLt}iSQRCWV3^8q6tSbtWtV!^ zH4QL+`u>+pb7^Gb1}nCU1m2yPH4Vm4k%9Pg-s*v}1dJ*>tZbDw{w{_XYp)*m4T(&4 zp)9iSrD=q!x2%Y8A4WEFLx!L*==6nqw@jxO!p%kKMYLj=ZZJrq%InGqy5zD(T}%!#BoADp)B zRIQQhfr*3?@pR5H81yzYAkD?Rj^AtM9S42^-rv)!o7V*kd4Cu$TCha_L>prPSN~t* zYP%rrMYQs`BelQzz>CXw54B*eI$#pDA+!V%mY^>FjmC{#$)X@5GZu=7MC$af>3MLN zbF1W|nq~84_j`uD8*IF>$~eCJ2b;NysMJ7<9)0utQF^kxSqdtC1xMT=!sSDd`S<6- zZ$g7sxLn?TlVoOSk7!{=s~_)d>1;l=*g*cpX}x`o$MY|7T3Q;(m3i{2W&}guog0Cp zTYuIeSWKKgO74tr;Cq*EdlLi3rj(`dGXR}d%#fOz8rF&mu~BQsv{^BR&0RFw+R?fQ zZ(K_C2}tC{bS8>+y%csj$K-VYh89kPC!(>-)#LKO&KXKiW$eAoAZi;FCd1qkd8Psnuzvc+Jaqh4`><&lpD?}I-RS)xfhBfT!=41LMBq0 zcP+DT2g~+Zu-!)|JldUwH>Q|ZA{jC#*Y*^&gYENQvQC_}1xpl(&j1R|t?5@JVmx)| z2~Qa`#fM9)c+Db^Jbm9axED|{6O)V_wOFtF-n$PkWn#Zoyy$+uEUR49Toa?9dt_+P z3q_Tv6SsEFMuigUR;}q$#E+A!!B;5Y7m8QnOt4_@V>qBmepNu`pM%` zjfzpnvp zN`g;_#XTrf5}nDR6=FUjkQdMe@ghD~Hg!mDigwQ@orS=J#$!t(%9oB^z?yU8J6mcT zkFjzowE?>e$WO12wGtb(IQtfT;SAG;?J{>LSd={*dQIphkSRg z*iU=3|JhS|_Nty}4$#A#rHWq&{-HELnO82GO8AM%Jr%#ty<$4&-i>qc^e_E8=}9d| zD|n_yZl2+#%(+3_sdhzA0)wxSoLLX#K6@&8Dfd{ZVbeL+Irxb==Lf?EvL-o?1%*AL zHssa>(HSl=Q_GbVKMxIjN^WCBS5x4?U=kUFu3~D91bqi58Cu1-wJX%5(X^f=gN_$q zMcW(WqNjo^pP4YN-<}6Y!|vn{bEkz(+muFyu)Y{Xoz$d+$oxZTn-7FTa*R3&5NN`9 zF}0>3mZGG#6L`Ne-XbpLv+w7uHm9i~N7U9bjz{(P#qMs$hn!jf2oNIa}x( zUeX$gV2&rY`H8Sz0fArJ+S+u5q#Bxy(zg7XRHuWe+K3%wO8)htos zH_>EAq_F;#zUvO{O$FeWH!YHE39S$eEr+4zs;$)LgfgoyGVpfT1|3v0UrWt5#2Dr& zXz1<8(*_r|5mrfzHd5+fHOsZL8r2lh`n-+l8_b6|&ave|-h40JmdGEdvUt*iQOWn- zI%MY>N9VU|5vps|G&Wu~zcJWrNSJfKPwcbCX~J}Z_h|FNLaV3xT#2lFS|Z6`i`Pt_ z)+o^Tx{NUhDT58?=G}K8Dz??L0qP^?kwatdnJ#-)t{Am8gDifoj>nl#9vRV zg=w4txJSDiy=ou&?AQMU$}v@$;EH_dfLAAIXLZiDg~Jx>kkX|**K3?(rVr_A<*O>{ z(Tp_Cg*NZ=+QHK~I!|QAmhdClg=UtiLPEKPW@fUw(%%QnyBp^q$1^?5rB3~{mpc`s zOyQ5lRSWSwjPEHw_2#^92poUf8kJgewkLh`{iLHm>X2E2qT#}YKb;=>4%>mB1lDJ7 z?Hnh-dmc&gUEn_#go)hQ8G5^Jt&-lRd0T4xvdU|tTiwkLZw=qOzIh%8Jh0IPZU?87 z70eK#NEHEn0*C)EPs2~u&%sQqE3Y7iO4r%k8!;s#FEwt=pHd*tQY`FqDxh8MW?i=< zA(AxK4m~)m%jcJ8I~9yugE6a4`0hc;@GpQ$IrLW|o3>l%pu0E|fuEU)KZu$q3WDxt zJxiUw9S7chd(Y;Ln6AC7#F#+*)3aJqLZ|H^$>995A-)RrW~YM{35 zG`f$*>|UOwlxk3KC(_LMUbM4FMd0#nM4S(|6ugV||lHw(o#H*R-LF zdr!dgsg@;*^aoF(VJIJy5|(Eh-5L7Lg_sVvHtSR0)B4b6YUGj=&u%$%=jRpXf%<8$ zO1g-Q6!+Jvg-sYL;v2Aw(qi=BcqI<&dWU36CTI~>U7{G;1UNoc<8YpXPZKii`4c)L z6^n*rf_=xfC{71r)~!R@A!6=f8@Jr$)M8!~QAuni@;AMeVqbw1G2 z!cIQ)t;e@=S6C4NUW=1++KuHIn!sYZdQ>ZA>j?uI-<q6m{1w#4bP?n*A+HQ;K(Ky`^m z<@9031{!5Uag-ioEkpcGhuB?XUndkh!7G#oQ?>^NzD{+*kAxD#7-2t3W*ma-M2|wF zHT;W*k$wfJZ2l}AVwQfYFBVMnYWe7uPZz~{We$6PT-uCtkz5ip1MnURoU~Vm2>ZAj zZZzzQz2^ioTrva5vtB&|;LkQ*&jxx>pw&(`_l%b zDSS8FM`{;+1QBnk!s=lm>mb-X>QiWR=SXz|W_l^Z7h_mh%H2%f!VCvv#e;kmQzHE1 z?DNQSsJkrJ7&e6-VV%z)R?BCFu3z}}bC+BHSd;Z@G+oTnd1 zN?G(&ifoJkBzk%9cylZMaKJ86?2T-qsRXYV4T~&~Eh6<6g)*7ulSrlR;(WfOIJgNU zfDsxg#QpNS7UJH}n+(xWFX)Jd)HOiEZV3AA9V(&_0?)hp5-@-`H$J~I8#39=B zVHm_KpqOl>!`+3eI(hD{33C@(zXpmWizbLr@}aSn5g7VjS|*xghe8>29rSMYx#w!q z<Kj4y7?CDCLn zs$jtfAamVJN{0%)#kH)Uhbw~a(^SC3kX2}E0lhTA338rR*;w+L^}15iw*x;(vKhdh z5C-%J+z?_o&K&O!*NrCb4W17DHa}i^29TM!Z4VR~@apRx+KRp3s?k z$0z!Zr}Fy0@QJ@rliKfMEcm9`r)fd-^A@Yji?38L)LDxM7<7H2-SKA4*jr>T6j>|G z@j5i$x|v(#2Ij3Z=qm7mmNMH%?33$vNAukyE6uy4j~nJNrr^aEI#MWX3l2z?gaZerZ>Zc6a-IQX z^Vfq2v5nrvH$hl0*oTVvR}jEg-IbT1dNY8_aJ6U$?`Ve^z|EbUT;4h0Ya*wd2T~q` z=kYJe4Y%1&`MV<=%HQ=#n7+Nt3>fM_ai{l}xABO?;DEvqfkT8y-8b*Gsr!Zz;DY zno}`818C<@;|Fv3^BEMPC-DS$Nksp_kDilSmBCUq23m&)oT6V0ggOUroA8r-`Jr-8 zIggnz05|4y2!7{aVQ#{<8fD(@9Dnb1F52xX#Bk+>sxdvGLxd9&Xm=oS_JUNWw z2>xuzqt)lS25q+r_3_v?D%0dDn3shhW&r)@ClMKDm{7|a!E0t?3r>Q5_smwvXwhzQ_dcbKA z3>wD~+yB!Dp{dpP2#3T94hHO+yV=%!+stzFAuHGU>(X zXXUbBodE~d%gAkJS3=HO8y4KmI~i$pM+BID)6%?ZIS?I`(7}0+zGL)|vr~>Osjecw zWBR4eaNgLuGY=}y86~ym-xb&->K`s|&AvYapv%DB-nZ5Bx6?wJ8R z(V2YJm3K$?Wb!`2<3U;8t>3CtUO%!*86?p67@JMM|ERmhzd^(yUOA6-t$=w_s^aAZf(rR z-lGM$hfb&N$fztjxqexED8~!`&_O%P;ihW&0m-e|_`};LtC)=^DhL$~Aq*K#-S$)a zD%J-YGrjDovJWrzo_!ah!c6~YZD-A@ao|}`3mYI{NO+zVF;Mtzkg3Y~#P8BJHXI4% zdt8q-Y#mSA{9f2vi7E9=SP7&Mc~*X%`^WvMr|H1w{`C!y1C()>nKqVAqwrxM?~E!v zc2|!4{&O4TWtZ&hdG#^X3s(ISjnR!VhRYmESU)N5z*=>Tv;5vsX1p{?&Z8I;(S<(zZDuG*xq-Sp}e%v*< z{Wc8Mt+f+%PUa|=5DyF^NsYE>er|YfnVH$vntdsEPK^1=n|n$fLBWkIkF`{@1v7ak zoN{sZ%lG*o<|O5K)59Om| zLvca_uy~dj19AU5p$8S5WV)p6ts92UH|08w)&M1=GL0pa9_UQ!wzB1!AyIo9G#f$P z8C_HFfy-2%n28f_x{e#&#eYZ)^K2UzJd%#gCRR`u$p#(me>T}0f`UAwMkBS(Ho#~}3z zOSg5c)riH$vu4U4I?r$3 zM)|bYx{}9akMBaB1hZ;9xm)6m!kFwZr5&|ZcD|cJ&xb4rx(4*WK$I-rzRmcAs`N@H zBQtN+8}94Z?|D{tM^)03YoI`cyNFy;cwGT^yYGGsU?2iG-TF3qdC|#r+p=~GxJ{(* zBDeEbJ{W8~FMWDCZ3bX49(y;!uqG`TxQv1Exp{HJv0;Eee}brD%=7(s(*lyEmw`PB z%n{$(TFIM*yKY+TGYo}(#1=jXkEq^bqIeFds2Nu~&BBN+Ke;xd>M>_urw0EW_o^#Jfb6=Rf2sHE6h@E7(D6M-W zAs3;-bf~B)R90lC-qzfx`FaxXLqrN+Om@4~^NCzF*!7Dwmm?QZh`BqL`A33q&p^JS+oIk#hAyz<+ zy1JcnvTbVX_qSAJdrLHh3ZDf4ue)zH_jRyGO>0Eqc z>z&^9Cv6UIUawjIx_r&s>Nm#8`?d(`?>~@;`u==bPQtaaDPUuZeNRlI=h_u=*QA!P z;Cfbak9Bw66tF)r23$7YxYKw#Dfj~~$awpcvpuP=c~QAKyILZiy6S2!XG`+tfknH{ zQ?r=?To1;=D>C5yAaK0{S%&3<+5fWFcg;p%x9s;HMl1_Oub+Dua#(6OiMvZFu^9WZ zT()yFAmhREeR3^$S2%vJ0LBQhck*PF)OFE*zIxCU2+HGl?pjrKC`5Bz+y<6*q3HKX zhKsgl9U`w>bP){l+bBe;^ z4Hyn9Qt0A0GvvD88azGsZhKI1B55m^RoOm4Hjs9e{;2Zqv`V(Ct5dXJZ2i;$g-aO53l! zB6-EeR_lZ|dTRh`O);HxdGkYq!nK?CL0Y{N2YNsHJl*BmWoTxv0K$?9W+{zU=k7%A z{9u3mXwM+u4aflBIrIv%l4D#>td+eJdMsj8us-0zS+A8X$tHV^Vg`>$_6RfA6owJv zrJz41)l;M@*~mLc?TQ&p!Haq90zIj^pYg&+Sl?@atyR5aNZd;brK!PA;+?|x2{X{} zinqXFR~CdlVaQ#{iLIFS59_!`7n)pOK|)7UH`u`diQyRmg`k^0Vo!DE$3t#0`^`w^3er0-ogQ#_6My^>ES|Ww zOQgK);jV}WA~Mnfsh04bxWwkoWYgA-t;EB;u4nIp&AoQ6I_Np#gbt5q`G)F_9a&n) zCBOrT8G&vIgKIV<5$@j|5FknV|o{F=w4flU{4S}0CU|2+8SFaR%KMXVN zm&&^tP}{{jq%&O3T1I+8IHPy%fQ?nMPH{!!^8 zGl18su0Cm@SBM2AJI>u{iCF_b?HFHQ=BmB2)+o~U437!C#$VoK2Kv?qUINxK_EE!t?uX3o(w<+Wh9gW!uMn@;3@w``XmiiQP~c%zHT z^-@-*wgJ=W(a|yv@g-LmodnetYEXTQn&`vdZwT2RX4YJTgPfHYKT)@{lwcG;=9N>2 zs*uSkX^_hcT952-XK1{y4yxOGm46d&ZZh%m6slrn`Yv~vE|N3(Q;VY%<+4tePb3fv z15e zkL2EG=Yatgwo)4=Q$RF@e9B9nyuN~^Pg8Jh8$`oMt5daPol(fi6Ttoknj@1)+HaZ#Z@w^Y{x-1~uz7GSX+(*6{^_SG`n3sbXn4IDW1$V^oyC?5+6{Qt;;Mto#ntRKHj<-q;3+vCGmkt zy0QLuX%k!nx_OPBp19Zj<>e{&Yx>7Af;k?W+2YUHDpw^U{1vGINuHRq;1dm+L|HE##45Tz>9SFJwcza@`d{g z^jW~i@8XYnr8L{C?$(=?eTNw%)p%c)h3^Hai;7N0TJR@QqcUOq1<29UJNT(0ray4* zVaVI-R=hb7=CYTg%R{fveS6Cwt=ljsk6TtHaeweg`C;L-AfdJ5_&HUT) z?^a(v?z8TCW{+7(L6mCw3VC2Ur*T)0ic!G@2Se*<4v+tAb!D~=&rNRfHOttxC_oxx zdHQgl&}h}@Bs__=TYZ$+W+t1XuxIgU&Fi^Aho?`fuDg->c8wRWc}pk7%EF{^qwGP? z2NC*#F0k>Et-VJaA0ki&i1q1)vZh9%pq>?Hk`KAiSGoQ__TB<2%C_qt9Y91tr9luG zx&=W{P+;g7g`rU-l#mjoksLZk=^;hBQw5|M>5xvPq)WPo8HRJ~6W^oXd(Qd3^^djw z=e#Tyi+kpd>)zMS-`>~0C=r3`Zf21{M27qAt!k5)JXgT7WbbI!O5+PSLi1_Mz38!AW@c9ertU)>{R`;^p9gRRi8m1FUC0%wnTZZPm-- zcHN3@Kz}+E>+Cqa+ajZ$#Owyr1WMCAkQsa9iz}vm!#22YnQ%55#okoWpDI}Qo`ub4|uT~mq zsPZ{%r~TLG(VWzsr8k^kds&LQRFY59cee!$`VXsn9o)OVbRJ?Yl3O@qtsgBBVHa=_ znJG<_Xc_N?ig+Pxjig9g-MyG=G0m0&`=Wo*0)xu$v~UuNW0PAxqOg+u^pVorM0jkV z+4dqL9&idyMpJhMH;uO=uye4<+wlcW=YS-J#IxdPWQ1Pe>AQ4n=iHD8?~)7`!I9El z%8n@)FH{pnc7%g07^Zn|Ig?~n?x`>`#vOj|V*3Q{@Pg2Zc3)WPZU9eOSJ&!OdSyme z2#7t`O4jga4K9}S9^?3^gBTAbm{Yiy=aRDsx%UFhUsBPxbcia$kqp#5uQchMmXp^I z&%eKQRcHK#t=5K>-_W5aSp2FFq0~4&ua*(hlbl|gD4Wy|*(4)IGR@DqmJ=!B%fdv_ zac|o_YOi`~dJyyQMaf}UCZ}wVGIn|FnL1^o6RNRvrraZQTrm~0dc9Nmf?7>K zyGT3gxk|-b*}8J(PMIB+u&z90x7xw3p~jIW>px3)<-dFzqE`24QkW;?~a#M%lh zFH_m{Y;mh+pvYH2UTZi>)HJuc-%Ud@eCe6F+HehLtJ`I~P7FYmTx~VH39=U|IRxHd z5P8U^JgYoZ-{W^|8tn~s0@fGsq2QpbRFd+-25^FQYfbCF z<@7zz6XK8)y>{sW)Oqi~qrhd4H51=D(UMq?-;SX38-NvqM}hR|R49(FqLC+qy^(7{ zcLsLu0->a2_T3$0Ahs*svJW(69r9t*jC2?l68LJtzSm}*C_r;x8S)MRCylV1B&yJ6 zkr4}jYbj}^=W2?MPG?3_z}6hUsD$U9)T$d2zCU=SA7N%Gk$VeBkd!X%4fQ5KeE zmG^I1(k!OYL`FK~YZiEaL({qbp@nMLPov3ZTIRnbGW$<29T;^{!MXNq4)gOs$edu}btxHFgE)^)HfL>$|NBidURX5J4d!JV! zE@3Z+LhTK{u2N!ex1FaMKarEZ)T=JM&xgSRV@rIRia^E$ChrM;ZxQ zl9nl!f=>!gtG{~lm7)N@NtT!4b{Y8bPS4n|A&Y{x*b_`dCjdwxwIyKNY-M}^FMUpp8rpNxNhoj1`2>LQ~qaL31p!8 z;h!J;0|__9>9_q8@BV3Z{~FR0e48~-|0QlhcKddHFCeh`Wf+cky-4Cml`*~!d`07h zHturV{502T<|m0<9d9PN-yXLq9vLdk8i3QSU?KrUG=&R`ac1Ez?q*P!s48Q>z5In% z&Z_KgC2zs_+mo|PC)7JHZ6kNDQ`6FUzKSpubU8083K)m0t`5JFQex31`kRJx-b~l{JQu5oE7K`s{ zpY^cka}8gL?$#rcJZmFms^=3SYKAtxdO(r;-RLs=sg z13lz0VL}sGoCA=u?>hhp{zJ|G&BPv!xOLceb%QC;-}DUh1u`EXNb_E=im_?S^xk<)b_oarL2rBdyLe~uS%ghlhhk4Q5i~UzhCH)XV_(O8b9C#ORx&MyKvth z4Lh)zARf%lLbFq0$yRPHV!L=wM=$0zlL^fKIy>G=7Ax}ykm1uITgI;u6)eMCrzj=X z4+X3&9L7{Y@0-Ddr5jydF$>;=3D2XcmGW}8Z#1Zfr-p9+s)9H>`8cFH2>NDDrwOo6 zjV(_gN7mm@&OrT7+f4VPFkF3Vge4Ppupv41*a(dU@89O4iaJJ?UcVPAW#0T0X=0k~ zB{C4$mJ#b7q@Ahx`?>$4^Y%Yk`~7#>=KsjYcMJ>?dv{{NAK*mmsf`$}?OzVOWK(x< z{u$T&Y17%{Q#XgO1n4&}l2C;>MXz5h&w^zP0Q&mCZ`$tZ9l$&K{&W-mkgwNEDzo#u!{j%w`B*%G06J^7+b0vm} z#;#zRy$u=$lv%Ld`rSpinR@gDSJHGZE0!8z&yuha6QWVda60TU=&(4NH1x{2CYN#5 z(5AT@JPrZG{$E^=snf`otr#hA)1=g_bN7}*C%jf6S<;JWOs7oe`7{PY(^vi#e*d91 zqB3AdRyaItPi2qs2yjcO--jfkg4=+6Vlas%(NHfzTF0>?1Cp%!L)EjN@@1ZOr{?39 zV8aXADm;rr5v}dLva#KzW!nx8ky@W~!LL4|3r|oB7QgsKilLCH++%{^)3HPd)d7O3EL}E3FEOuF zyYa7DB3Y$*L(-eB7p%ILYAv6N z*Nw!uTtR6b{Wg5D#-LkrjlJ+(e=N6;s>lpu?euryxp%_UpO6@vv7WHKCyAO9gr=-! zhmnSFN74Li(P(jYIVd~1q!%B~zjESM;cy>ee}|x~rYJjuTfZmcgkA&eg*~lYqJTtQ z@a(Coa-J?{EJJYR8K^zWcyxKPe&QP^^! zq)6_W%8ZL61rCR#HMMn%JR13Oco=;f80=M)2O=HG!j{89VGNam2~oO@5HTN?9vkB> zjn88vj=4>GJUn?a_+s&94*^`>l#!IAL}f_kd!|mmfg>x#CeB9bX@Z>$sU|Yp5>$|M zaafamCTooe$l6_iHzq+;XkK|aEDlLAXrgFiSmM7`b1(X;8HuMzInT|&LUQA$m+7C- zEfB5S89cn#S5~X0QP{VEHL&9`VPW;*(+yQVxPDjLwM<}EpZL7Qhf8swO>B^DlE-1D6 zJTks%@{lAeIX6CjAi_pn(#cM+MURs7jc;VQU{}od%n!HdLJi*@m9chQu`5yi>jeqL z&5itFho^>%uX755IC2{5RYtZ_7w%qK>$FjSlc8Q9a^oi%47%!J#Uqp4CPp{h#|4v?=CfB+Cp2;zAK8TCVG%B8&O(OOS~J*Eec&*@}f` z+#^KYv@ zP;chKrWr~}-23{YCA9Eth35NE?!(~+(}z5<*Wc+1bHc2MaPmFBS|-)6 z1Lnbl6IscDFFCGDoq=vyDSe3Kjyi;p0X$!c^$ULfHsiwc<%)-t3amESK;qhZ>26?g zaco_Iax-P3w)JJd^4&d`cLJl5b4}W>rBw{679g{Y9`3hf-;jPCNR*M}*86HCAxS^s zd9PfF6~s1;Z=Wqmvuqs5xbvt`EVAMpSld4nSfe~YDEQra@>XI&QC?L$_|uM~!3**W zIWml8P`+&s=`+ydGmxfHZ)U#G_hZRsj4-^>pJq=sX}(HEw$RXv39vV$KmxMX01jw; zLZBYKX5>`OtV>6pLy0vYUFtCa zuZ|xXAIkyK_{|j|U|#S316;0+ zl^Z6JrZm>6-zyvQ_T#_{8&d#h^Y}c5w{%G{d(%E&TewLxHC4v4y3SX<-8L3l*QuhG zQfIW3D$4of48%iF_qCE5Q#2MyohcxC?2T${w|67rA$rte~B&75+coIZMk_KIqY2o8K%Ko2Uo61CHJKw3V zzNL{EOI^rIJP$4Y0!$@f6e6c`_R2G-AIFiWgh5qd@#BY|CC9a6Ts5oOsrxO7kKcl; zCv0gCKMIu|)z#ZEJs!3;TE?wGb_u>oDSJc&4O`}ek_0!%4x1yMRi3teyAle4KsLR? zM@P2@dFbSJj=k!xe0V>8ePccH{eG2AaEV;cCU`RyO?a8vL?R)ZbP1=4Od4${bx0yK z2eUCmNZ1iJ_6A=m&W3iQkO2&^@8Q=fsfe6=Ex9n^?aR>joGCybe0 zROUUqZ572{#;X^!FLLvQK^aM`5Gb-WSC@W@?y2V_ff5-EL+i7LwN4x4a=^Naj%W@9 z`mJ={NVhAg%_BZH#- zLbJB9sW>M`bYi4*O^Phu%9&kx9~yG;^PTQjdNka_@w!MA4`DxL)^zVfrmIk;;g!m!K+1H26Y!N5kzVR%4U&}tSRH>kBmLzW1yyq z*^*MK(2ud|u_TPds!BO;tC=vj1&3}nfACr}?+FU+QP4)~?C~!d@E*wKHEnSjEZB9& zz}o+l!qru+xe>)P8;P4GJF@pC!F@lSH1c?q7;WXV$DVhhjDcESdtF@qt z&}a)`gfBS!>ONpkZn=c> ztrBhS4CZ!Kc-UrWA?pG%{JTfP@A7SYCI6-Z%WNy>>Z)*fFhwE&`4qv*@NA=ePc!0N z?nw9BH;xJ$-S{~-c;Y)oKC>>;j*iZKfR+#(GmcHzp1gRn$SZ24z#e@8M^t6l`qH#g zK9n>li9b3@A8l_ZTinVM{MHE0y;kg$OItkQP)F@FO>C^egIQ==I8MyBxvuyRHrtkCwArg?wWM(>J>QHs!~o6=$kS;fSQTC;l?NJ7i0Z)@bG&y5UHbM;bW<_yV8i2Md<{(J8K7v`q^&3gaitVgaBNJ-I>*H45whD?US zQU0eL@jacC?DmZ^+St2^#ZR0>^`w^r|?((t##A z#*p}L6w>~zebLVR9>XMvI^MgErhy9l*|GsH7u2%#A+E6ymDTo|=LqjmMIWpO=M}|! zdY~?v);nH}7uh%peVn8WqZ5Zh)$_LQz3f`qsd}B3YBbDHXH`hMlUsxqRbbADwgeWm za^mUWE~c9x808kFlj31$a@XG=#q)bODjeMw?YIEBcqnB1$@q_BrX!~L)Z5dL`Y{3G zeV{jOPFZtqY|mjC#(Cex)pc^DV3wv8TvSzhB*ULGSEWRkwkObQZgKtXXg)X@{EGJ0 ztjPvcSrh3l)K(*EYlmsFP?e6AOUt`^uxYI++3jPGvB6%hb=M8CLLZMzIS#NWb1(HH zV&W3&o2N;lY0OaiuM!6a%L_xizbxj-<&7yL7EMELBvxl%Ce< z^TN{j6N8LdagC=5!){*TOV{c?Z_##WHpM~(>8?#%cWTW;_qU~tO2kU4yM(hCxcs~Y zCpVXc#xV~E3vzS9m%OTP%GR1$>?hL_lmmWXDw?>}iU|=pP-qi8O?AimT){O$naPQE zjjHh&?J= z`Lt3ESV;eA{;rZ40w^8lgmu2xB4|)Si-zJ)q0GPUNK-q(|r5R zK=G|z0UmN*l2rvU)uji-mqX{&O86#NOSCJf^~eptFScoG?`JLZD3#uKaA>dM&yCfP z-_f}E?O|XV%L8O_`xd(7p;jyfS}yR`;GQXSB)s#E=%n$OIN#^j_UM$+xGqg(BHOv) z?@J?Lz$>X>-FzuWi7oPkua6;9{t{rWA_0dPNiTB!xiEo%D_vR!~3_JSoYiWc z$c{l)^J1*>sauLXg)*o1;!B*Fb9o8*3Wq8VC)ii~RG?63&-o-E`k%Z`!+SA#xL{=*OfvObW>72v@s@4(X$7%Y>Wo zix=>7V(*9*@4;>u=rMeUUbCDxAiW>D$gaa#v(~bEj~>PvVrU&%>BY2IEhiBzTSw_9 zDJ5s@|FM#iy(2NStp<%26BsK{cSc`hSUwe{v=wDdh>ZvmalJbk9r~O)`+_=5N2e8v z_hr+?fiqq>t>n%Di}RRbZMIP>quXT@?E-EsOv#cdOZe4*-9gk>HeW}9?=R*3nF3l7 zhz57d4d(ly!kjvkNo$g&T$0uLZb5dkHpSecnL*1hCNs;goC5T?AXP$dUbmbfg51-S z4<`(L8p%R!R3LK6Lw+xZSc6MgxSrB8U+-LA70|BlfyM>V*LbJ4w{P7ocRBoypJ&9P zjG=!}wZxv!mXr?r7-p^0U#sa;FeHOz5J^s812`cnPXrA6jp8rl@?e8j9&|HCx?FD3R81$v93#Fu%lzo5V)&KRt@!DI)D zzvV~Q5M-7+5CF;M&&YhWC_6H-A*KoV*3!=)--5q;FlU0yyRw^2HR z(Eyg6wx7!W$N%W)>q8kY^HvCMauG+T zNR;V0-qEJK9uswZ`rQhdmhO<;U;qJnDzO!%eub?4l|J7@dJkkO6gbKA3}jgA5$JU< zou=42Zg-CqjOb@LMapSH%%Xml*bT3sgFJHNcT@`Ns|y};>VsjGADM`_q-9M;1+fdXc0=5vv$cf?H$Kde8MUdh)e?ZF`@ zHO)wLxuplQ-@Jm%4W;4Mlg1VX^R<%jMkIKzW!tXlkqsO$g%QooM4zzPOU(1zNJq|1 z9-idzT;?F^ROPLTcynbgX%JX;!vi)aUNQkMsMtli0ys z##KG|xtdlKhlxZzUc7cTSgH+ihAN$~wzjo%Hlv;|=QBCUt)t&hWGgj=c~ehG z7PW8E^^(S(nLugi#>A3T>L6DRow)>k zH!N((V0Eu!clL0Vy}=tqz8)VwwR3-$S=^8^@1k+EQ;S0Q5Cl7vg{c$MgvvI2GmZ(Q zU2PFy&qIGd1h!JhiJl^eDy7Q4@;-?W20VEsC;d=3AHFX4jM?%hK4Lfyx3*DxE1=qiiRikZ1^@v_%W`AR|ZB9r$u6EtfA^~U!H zrb3>3jhp2m;e0cjL?QS>#4A5Wl51n^sD4vFmyr1kRJm>OJofRVT3rW|9I&z-GDW;$ zvFW6_u@=i&Lh78yqw=O8nNKbgh#j0bHc%XQwQ!BFgQ}xTRe{-7c(Ua@EMFdQ7_{p1 zCDgc&mIEPNWj-Imok=7o{K~*BD}01rq=w0y39>16n77W0@Y#MMw8QyWI z*4I_+gK(QtcTK8%5Qr=%byvLe9~!eN0Y0NzH8e+GStY|=lG%M{#F$rP;*%eR5tDfV z8}6o^=rP&fN)XaxbL1p~Nn#qtBpv2qlDwwKtldw0Jak5$qNK*Xx?FOx;n8rj|4`MN z#B92}WS6=~QYT<^aW-i9N2a^as39M7%%UE0p8}ll0~OH9T%rfcg|)a2VJE4-80~{Q zQcqIR=EDEHH(b{|H<7yiv>^BA`&cc4{6Nk zd4Q5Df1vh^2vd$DJF>!F6KY@RB8xHph4Bt$8(th)V!@jO$B?PaZFsHH4AAT?q7kz(Vi4slq$sT413KWCmpmxfbjvDLKLd@& zmL5O%cc?f6UBXoK@In1V&$)+V`|b>F5~l|sQ}6g<1?nP3Q#U`vl28CnkFu;Ymtu7| z<%JGXib7TduHU;6dySe0SE_Uv?5RY#(lC#?4*!Q=gtK!=@_5-b@Eq?Ulk{m98s33` z{2bNN&KZap*avj#12BWR$A%m5A?>+x27-c!3?;fd{O50QXe$qIqp?On$pur$7S8y< z>oa>RYos{wjO8YCb+u6b
Y{lOFMR-+S=4P6Lkf9+=#>lxhIeVrwWTd^u?IBLhOha@s)NmJS)iZUyA z-S2^R2oF)9Qs%OQ-skx^&bLLIns*la1b6Q(=Pa8vd0{a{JG?jGmKw_!`J$xK z*-PgS7F}#ZE8qruc^U_aXy%Sgx%Jm=v&Yvz>Di=?W&?ob59E1 z#|?5NW?1{@AKws2HxRe$;Q*G%u)h141X524RnpMQL)UHEE`)ORsPKS`_pTQ&#Ip79 zu#3ldyx^Y|Dpan@I!X0!8|Q#3MN`7IrL=bnL~&h#r0TTM(czLL10fkQ`hAP|*}l#s zv!8>kc(%ZY!H=U_>L?2OC`YK1(uUUI1fNb0!C!4~oq*x`v*>k7{=!@a+UPjkV*L#C zQGdpm+H2tJZ5KpF@QhNr_4J|k91@2k6N|{h!OWT+i!|6f(!Z^iNHXLTqfZ21J{7<{&1z$=C~D@P3e&}ys-S{ zb@~8?fyvV64NFTgNa4!_vwAPEmgA1vMMz|{O6jF*$)kITTnd_I#N6z{$gZpa_0d!+v}P@4VgqbL zz#983rW1&wDQ*uJ6O8dLcCfwcZksKd6=-gz*L@jO7n^?_d}C3y_lp3#n&?C0Yeb9L z*W!+PG&5?TdJ6VXnsjO~QQdR7T+9>-%U6>6bzr<;KvB3_J376EB(GL{(>r>`z#%fT zRoWl-?aRh&Jy7TS`l6=oe3(JKXZg^EN8Qb35-!bXj@LF!*^M{qufB~opHmtn^zPg< zvCVbVsc(cGmv^r8dxM)6!Zi?s8Pr5k9*@f(hNz_6Qwx@BDmAN_ypt3CTod`SKq8^f zMH`@dSkj>#aMR%iKGh9uMPhqX4cMmISq#Co0sc|T_XC4MW|vi z?Yr(Q1%`PwHs7v*VXsn$r9c+STqZM9Z8N+=;l?WOs1Nu3vQ1aXy{A0dPt(UUKh#F7 z0~YC@-m1swOxcwfd5D+Jpc(O@^xWxIpTrr+(6}LIMKShr+RK&G?=K!kDrMki*5BNd zA5q#S7_NBshW7&$1&1W(_QSb8m{*In3g0(3iUoGQx2dnJQmQc!Oeh_vx33@l}$$pdNxDyLiuLtZONICFKxEE*eKZZ@fmR4!B(J~m0#GJZ3KGu|b30dMw? zdD*h7gsCWh=PgNPk8gO`C1W7QJ`||7L+S~ah& zp`52cUcp&#@00~0o-n_xB$`2){B&NU@I$$uStmbxAv=R_vD{LK39!~B$8>m=F{%Z+xTcOh*&mu>jgLDQ zp36`=nmqWtXQi<)uRSblLM=j|ehKf8yft9oEF^q*i?1`D$ z7J>?Q3&onC0!^}o?YRSQOF|cU)y(0dR!U4O$r}7t7y2!=b{zB@UtB8SJYD9v5-XOL zGESQ6N*$(|QE;znk_NF@Efc20pnjs666R8+WdFQOi!qbEsydxpUaG~CS{eghRiR9= z)hr;h$lP$;1hH1KMl&PQWs$Rbn;OkDMKm25?DJYXKP>R1p8aVYYCZMf??n`+EhWQq zwf2~g38sf}mZwA05p~`LK={359R#!D47*n&E->T0wRw3+<^_wa0Em>Jo2<&@KH`Fu z&d~eCyj%ehky31k>CeCbofcpO1>qjT=K&`%!1#C_HA`zAt6-x?S*)`4dAArS6yruH zFf%bTzzYXqQD8@-GZ6FGKO^Y;Wzj062Dg15bjElU>^@jMyR662$`f4*Kh3(J443k_{bTEPiCf3Q>*fNS~LY4EnSW^x;0eyIao#;@`L0DvW$O*9y4OD532o=w?lza$a-bN%gsv}_Nx7+vGGCz$B} zLw~Nl^t{(7)o}{{8zsiP?=2yoKSX&h2}+b@OB|nwbHNCjH^T?@pPRKjr4E0=J+i|T zSTMLDqK>7OR5EXr9DmkC6$jlL3(6Ad>UtpmlFm|2zVBl@ny@6*w8JDzjQk**=?!k`Xs}J$~tPJ5@kgWxHfxEWcVc*fZ<=#e=Y%f%dN8fUX=SC-7r1vdTRgJmO)FL>mL8B+9Qj z`IF&jJ3-=RWfjP342>dO=?s+d{uG64-9G~zz26wPh9>H6KLaKH_$X8wK)pX;1phm~ zkkeT|pmWF>s3>x!WdmN&$Jdw}+?!^S<>luG;R5dLC)n^AtNq^k9LK)@8K~ve8-ET` z7v(^k*S_cTP6fIfQ|r? z)1S_ppMk^=+-q1!WqQTDMhTb*&wKi>?RTHNG7H_llgrPwFOO7_-f=0wSCK+XC_d1v zWL@v!;Wl%%?|eh~WuTKn@4#1j*(W|Yg+DMx;;PUBsinyPdl~IZGC^Re%9}+U{fgLy zfdS$xQ&<*++2bqEsS`|5CkJ>?f6>^QUm>O1N%;}ZSj)rGlrW9 zvqmr_53Q^gD5qV(YKoPTi|i9dPFDEV)%xa8LzOcSonZBSuTKx|g?@@jd9i^$18u|k z&Op;Mb*Z0tfmia>D4v@0?DAeKZ;ygApN^&oVbMZNW&rV;Ps=r;_Zc|+1GliZYAy-4 zTPqx(h%DjJ;hu<|-;FQbBuWxiu$+K|)!TWRm<9-L|$Mj?ULX*hDs zyS{V5H)fCcpDKb0yv{%;Zw6*LW5PD}9r}gd;m1c3%*wwhWZ{rtX>r7yfodO}Y`1iz z=wGAkVrpYUX?w9>DB1;i@~-lb2tedFHX37;B3W z5xfIgmCA)wlfwf1%vUcupMiV=ar@m**4>zTe$WiyvXJZ+;zpCZFG=d^0?v`HNX6Y| zwM05KnKr9@TJO*bqeRy+-`7g?I~)L`@Vv`i|Kj?TJVD1&qYxRv&L_x<3b#zn{myx; zn%675CKm1I0sE_<;5Rd2ctM6`f6f1=M^$ki6rbfTB44&svU|f1+LJmGI4|sPwv-R0 z^D7}ut}W$qq;(L>(lQes~Rv{4h;q-v+u1Bao998%b zW1dBn!*_$y$D9-?!t(Fzy{r=~FOFWk;!12ee12`BU zYmu*h=s=+FJg`g9HSL8#Mqf9)#|tU+mBwa1j}xc#Zu<7K`fsXfG1a`Q5>{r;zb>|` ziPxSc#7^y`8n4Nze)?(7t*S0broopz@vd1*HHQ2?djizI;fmL%uy}Urq%N^q-!H8>wI zzpM0VYi%bPe(XV#OZ>ROF={3p3&Bxwx>B(&$I+dEO3pyQoNe)F+>%pl*m1s@2rrJJ zzt8a&Es7J#&;gjO(eWvOOJ8_|gYAEfLN+}#Rz$TSp(q@S;~0Z0c)#FPVqt(3=cF-{VWcIJ?glf6fA^&IIq3P%Pk@ZMI&>&bj_Ma|7&zXkRyN}AZT=v}+EFp(c;Eb%an*??NezJ@<#vKu+e4>ih)nH{` zj|j=HCTH3AJB7E4QAS^#)JONFK(G*C95XX*M07lsZj&_d!SbwWiY9{OGL)5l^1o%~g8W_UnAA7w0cZ zwkm%rAjrQoy{6!v>h(F^yw8nf{N)q&{DQHq+DcepFDaGDj-;ecykGE)7(k7r?lu2c zI6>?g2s5wqzwd_*13!KBL;CCe#|T|lq*&qDK7bbv1%cT?|MXWt7R=woiGm^* zixM8e%gw`aSz%b-?D>X!%;J=msqsJ)-XQ9P#)U3$Q2hDFC-603hX+C}&}IF{w9?b} zC>n6qK`P23`vnp?JO@7Vj4HW+?LF^$<1&xCnx3e(Zh08{j>%!!r>2byAp*Go>ZqA^-=*xZVtWJ)b3OCziXefe_UbDC^_U*tNrB$8*yAQBp6f%$|N=-G27|KqHhmv)L_#keQy| zbs4bTe4{N@jTSIjB@)Lc^zkn*cYi~+w>)qa_Hyt`AIhGg(BKXdqLL```WijtIirN( zQ!}(+5M!Vl?K_f4r1r;$TQ8%oqzF`0l7&-491N;qdYI4 zJw}0EYg;CZCzHW@IPi1@{>IrPF%q}7^tg0u^Uorkjc0_H< z3BgCySwMCYIs$2zHHf}lYetfwiB$3Lb>;vDq5Pu4Wc)(wtd*1s4z*sJS_MA@|3N7X z-4E}^LPq3HDmB5We>3)>EM9YlCy-xoc}y;<#B()S0{4ss!C<-s{!mN77QupD+H=)w z5sWehoI#L1Mbwd>{j8wr*iJ(X-Z16EFQ!twGlsL*c}xRHCBE%Qy}VOiss{eX~_ ze>@xAQdEAEI{Px6vZG~xNpn=nr1mhA)tWAIh#x7VPb7UXxnpzlT0mOyR{F>S#D&tx zQu17W3M;qz-+SfWGO|>>#QC| zEnp-5D;|DF63~ZBs!KiFq>h@DYX7Q&Ho*y z`q$@9PcXXP-k@RU{>;ghi9uoYQ&2`0DH4mlU0G-i+&E?O{>HHv9Q@NS{K*c7U(P^x zXW?xx;k|{z$B+ofHZ9BTF`VZKY96`#9rqJj_+!>Twxhq1VEcW8K|sO86**c98OfU? zLxlI*fs^`ZFMkpSEbZ7)pYgxCci*-YWclef{Fv(>w4-`WfQ!xE4pjp0;_pOK(F5Wc z3D+$6e(YcbZ8QoGtN5NOwSTHNY;<;8#z#%gkzm zXAWQ3>SCP*5LJBS-lVT$=u=q592rNtwV`bPBzImp=UEAb ziS$4<)`luKCi|=TBO5dHm0}k{ymDLxj;vQa<2Eof+MgD&90gW@ zKgE5iT6&0#2YTCG+b;xUd~@5xlkrayq$R;Y&+BF?=^1C%Axcs)k?+&qbpMS0d$kSq zb}f_3kC)%pssXJ!1F)7)$zP^%cOfJ-V~(HNz{^70ZyPsM>ldzeS(fP2bJr_6?;x+f zGmO9vID4kpMGS~%vunXTv||SE+IR^hn_<ZP*+Cu5|iFj%{(Rp^S|Qvh}!C zONKFeasY4cGJS%RG@9~7-z@$b$lFBPwU+U|0>q`<2iyWH=azBL@J-~DVSNd|%?)b@ zl9_8@%=iAN#)>G6z_5HZ01CRv0vr!B_b6Gg7aA7J|*)s1~@Au%YSITf{uQNXl zO|4yEP3+*dSoi+zs(V86ICpAgu+*6DCcoICrIkr1Ei#|W=04UgU$yG`z^*qSnSb~q zZiGeHEk=p*k)qSk>7)iK?MhhR?nwf6NJ7rM)LTG)$u#nIIk0spJr7=o*(-$?@#cP6v}z2FidG=I0jI=N;vDJqze5_5B;`(s^ z`LPg`TJODi+#OS$>p2Pm7g%G|-@_{fuL|?H9;Docey;u0xkp|I=dWKkOU!TddKhO( zDd6)lvxlF1=K$_3-KCIz!v1mH+Mb(p2+FuB;j{Ba$TtWb1qVCdyo5?5Iuccg?KY)d*}lPANKB~B(xzG1;7A&eDr0uUJ|jA zC#v0QQ;iml@Rs+km`!zx3p0s^dw)E0OtqZ@Ib|oxcOas9u)c^nN_nWTn%-zIz_*H^ zBloK7ITot9mVDcQy_BVh@T6l|3ymsh@Z!EDOc304&cIiNwnI@w}?8zBgIrKtw_7v$J zWE<+~>^8j8X6SM%j%G#bJgi}459VyeX?|7<<}Tmuei)BAywPDP-e-R4$~DPjZBmWF zY&Uu5tuo(>zJ30vo;$Aeg1M}-%E$A&LVZ)oCxc(*482Y!^Y?7`QtrzxPLm#G&wWqH zyv9D@K9}PJMrHZkN7+-OnQol@)f>ZD^K}^g4$6-BS=K}?*9AT|qB6x+=ZUL^rgOO7 z8cen?cU2N!C>Ya>r>1!uQxs+)bmkb=xN-qR6(6|Y$+ZWw=lPq+6OXa1OL}`Or_(JX zRx&d!jap-ZHi=WqWSo6cbYqae@dWMJ$Bxh{ZRy%2b4499a9*7s;}*3X*ZoArntj*& z@u-sQd1Vi!>a+HiFBSdk@5tKw3AJ)NyevN~996c))qFIR2{#v2ro9*!fBcTge0k>^ zr=_;O6WFQKcf^q*u6ueVSq>>irYk&fjmtf@J6W-9&ME6uR=QQ_!vqr%tLH6m!{rtO z_)jr-D)|l!9bNKx7)DoHb#o^iOVW|&y-rEK$zJ-QXpIXysFv|a?#x3D^nOzYFZNh< zZg2m)o;L6AY5L!MJ8@0@bOct;A=?sRhdMs@K<_+Q%t# zlq2!P+bTN#$#Sy(!4TNsFw znueFOE8O+&g(g<>eyCHUxa2_|&gU6SUJ<~$y^H*+`XpFoHK6q_iuLvUT6zpl3!ii30BwMbY9%e>q9U69YQ{l8>%@9In z>8sZS)JEpGcX##Ncwc>BZGd@0VfySK{mFdzwAJ^r+5nbgCCsY}$@0KYXym?ZF7q_M zOICGBV0uqf56xzDrE#>nfq~a*$)JWMU%(Ty`*NyDL#m{1cb3_d%7}=l<)*}=~2*Pftk@U`q+HpEf#U;_p>ALa#S{v$(C|9sg#2$pNXd3K(-dic3%51;)rvujx9XgJ&z)p|OX=2b z4P-#W;6rZ9;}%Rz>yR(U?Ko8fYJ&=}62$Bn#JAHp235r=GwX;a@`S6(Z3ArMg(fZS z0I-*qYNKZ-(gCxD?|fvs{mL}Z(`W7IPPQ7&egv8hk@!$sY?{kEyC_4gVU0UhYeuk^ z@y>Py=34wfxG2rNt)qU@!v+UgdGTXjBKvxd0bwD_D9cDY$yl(8%;^Ps{Y&?!aw6sV zb#%exy>yG*KDZAzYa?lAXOx{NxYuyg(gpoewr|uOdgm4CIj2(O&NA7pLy-z7Lo=0; z$*UUG)(vL*F^F!^};i>~0O)0*n1H=O}`k81_`)lEKH9tOpUwU7r7KV*zd z2GxmdseF%O(O8Gh#|Mb;)U3Q~zra-{q|DKdp~I&pLHr!kV+?CAMkx@ya{W;jJ2jX% zyI|lD4p&uNxc|2GqaO?meVgt->QrXn5u9GwR(} z6|i1HWBKH@Q*n**Oc0-eBI#J;fH`+ulmzZ5w>OUz_ zS6#xMCOK6uRfO*hpdE;BZ8v36o0XC83zwaNJWEJz=dBd8&3)vq)9DD{OX99X1?7W7 z0R;(72rpr>!y=P<1ra!%Qqa4iQ=?cXrXG50QkMlFHO=a9a)G^S!CJZzpVu z$Q_TI?4(5;o#&f7dMJvDV){g*PpXSelzGRa=KB_hkDeSrbkIJ*v1fqvL^QqaU3_9R zOteeJlvOHgN?Tl+iee%}Ml|~oL2K@lVaz4B;Nc^F_4D}=Z^!D){4BS(CG|ZaW_O!B ze;{&mic4y{VJf3R@sd`FU`GaihP2ZtWEV zT*O}047GY-y*!KKl_JtC=yzUa{CFJyfkBAEK%Be&Ahk9+--~B|wX)6-dZ7J%YG~vO zwtgO7ZzF419lMI6L+=&Q0YfYKOT}AH4(bP&40RN8M+9^hetOL}>+IK(+?*O3CKvZ= zD5=o2OhBF`zFGJ7ko+~yjE1)`3ff?ubSC^M{X*Nr;UUmBV< zax3$Yz3eJ3H7U2f;9i=wt0!@`^t~+`CI~OQbc~2DQQ?R4ahr&qMJI)XS}GKjs0vHm z7L&*n6I}Zwu{S1tE1#ZGn=GerTy=SGE~iA%%%H=R9EQh@cezB>(zj;BR0M5)dx&qe zSGYh1Zol<}2pwgce7>|WVb^Qpk`g(lk3RNM$^M83O73$jH?t&VxM^6YL^aa6hJ73; zXhgYh%V{znsf^HC99qlR6_i>2s$b^uu6=gcz34XKr?Nxor&?rArZa=B6|Ui@;NqZn zF8iEW0-U9<*NH-B8-kwcN5pJ$nmZPdD^=m!X2pJKl=S>zrSc))BXBW8%ebhqHW~`k z0FK~A3*l)UP3Bfo#b$HT=%Pwq1AbazNfFk(c8p2G!2cBQnMdE@#jW5{UF}+Lt zF}_Mzzfiorf?WUA+w#Y*_%3GZLvDME*Jiw&Xm=cdtVqaKPEMz_);3 ziCU>3X_73+IpiPpe&FQ?9sG8FWGsWoV$FDx)>omSHF55Wu%8@Q%r)Sul)HF8hh_m! z()e1gDf0%`rtZ}NLYl{}L!4kW@fpYDNdvx{_+6{z_^3J?eeFCYc`%*7E^f`^=iZ&0 z!mk#99qFKc*GvC{vrCsI6OEpI zUvG%6mF9gFrMp>6-VE?S0xf^C2_klXzfk#8W#X&XAqAY3ydrC-Y1>C>>_viS`q=7S z`RL!28Eh+LO8liK7Dcv{#*W2?J2*@3agK6*(iXxUGjz2@CBK_V2a8F_N; zb!)NarXnHX^Q!Dq^IcYFxz+n|V-H;$U4<1;jfr~);++RI@NVkGlKDd0SFhb0E~~9n zG-&8sYou2LtS9q9#{RumXdUiqB4b25*)4+~huCD&Ns+qynnrqkcp6n*M|A4Z%9 zBhRip68Ac^emr5M8-5M%`eN;}X%bB+aTP=ZmSApoS6RccGn8S*_pF`Q8x~R3VA^|v z`{e9Z0lfDK1NarIPr#u~Fb}o4Y^EfkH^3jOcv=2KrT;!Z5!y&1`Rpvl)iR$tL{BRf ziBG+Iz9l18&qolf2jM;sU}zl4 z?H0pTg!x^`8{`-#^A!_2Fl8JBuoH*%LrC_Wp-6H@1=y^UNH$2_Ln}S@6z7%(97{6EM>pz}#|Qhvx4> zNIWe*{r$K(-4g3q-JIrYa)nzImd8AIVX)j}3)3>bBI?^s0h;U3o>4NeuKY~HL_zO} z<|@bAw3;p*&;`<8C?vl{>M$_zMym1s2svP$vB4bs!S;H;<79pTrRz5ztwZWMpgkr@ zREOFkQVjO&otye_e_I#;*Zf&!+%jWpxAxQ z1B7Ggvs-@r@vygb9%Zbt(&9kDi*0x%z+?-Mk_vh0$LM!HpZ33N^-tv5VGSY?fegmEi>*R0)CePNjzArB$)YtE|)i95ga4V7W<*~%1i~8{0 zPu}#+`QX=__g@WSU6@fHo2Mi_ZX({Q^6GLnvBE{Ie&8M(PBsk(%(4*vbOo`m2|=yq)tV z%;kLa%1*~7m~;ARCU$c}B80x!SZ>&I`4NXo%Fw03q32t3KE(x#&KGE6J$K$;+}lC$ zH(p#8satZo-TKjMP+&UG7UNEz$;&J_ovZysEYFB}vThp(#{ItGPLbnb4e1P0*9NJR zio6CTPtmyM?o_?&n)?#Zvx8b^C8V0FJr>lnrUjhD zEus*kcrd5TaBYB0f003DMbP7Ww}@x>)bS1WEEvt%DH51=W8@Rh)2nwsPIu*7CZFqA zhY}eBwCv#(=IuH?VF!ECi;`+4uZ5xxp<`JD7xuo6-f@N48~m{21`MUaqj$&S*on}nwhDgKL0N;5gLf+slrc`rts{T+sc zB>Hqx4LsGsHV>);O&ub>!`dC2Y~w?xUm=Gt@a^Q{N1_yNA!a)`>$G0`@1UT$HvK4f zDwu!`aJM&(4iVClP4g+(OG$y*>qEJ;r-zXa!Q`yZj~Sz~Tri)vJuC??h+$@?-c_(t zm_>29(S3-Kj*5eSG9vf-M7_1GIv&nURWV@0# zUNVC;RKJj7x@~@J%s*=A^B%mpXw5?mE%WR_)rur#VEk;|YqG6&P^y5n|CPGV-oqtz z&KGdK{OK^?CE%fQHhyt8Iuyn-EY6SPe6zb%k0wIJ66)CV$EsL?EENhAZ+IeB-qJozcuKDS5 zpUvL;Gl}jVRZqkeYHA1rc9CQU!zhV~F!u$+=`19SPuOi2`)0kM8QRg{RRYsFrJ=Av zgN0a*)`bL{5Opu7?EslER!R>KPj?w@4o$r(0rbCAJ9FIj3&R&fXFWDcCM8SH-S%aH z82Qr+snn~b=U7SIm66Vf8L$?ziK95)Tywe3SYJR%E;$|FSh5a1I5V>K?7>eyCT$qW z%2Jxd+)M@_H-F8_I->px8DH!xljX5rE~6*%Np)iiWM#F_-7~=%?(jx z@nMd)QCl(&V%}3PTVG)MbgR@>8OG$8wlrEVt9q7NWt3%+k*uWZK~{Joa4sNRURe5B zjFwj9%R`~*@5r{(9MVxdr3ZoRmQ4;No!>NxjXL!r;&QbBRnu0}@)HwByDTi$t_93T zH24_Io!#t6|Ju^GBcwD+Rf;WFc=Vt(1;;+^5VtZ}CKg90RB}iMyMn;&vwr0y&!D=* z6EKCtgM|U;=n!Fj!QD&eXATA&V?)EN)Q_M(%G_aZ=!SiH_Ho*gFe6`X-J>xb%jX;gf_CeRaM+jT)*o^WyaVCl2JcVj%2@x zObnOK0P;?t)eN z;K!fsDNhhxEC&F|(HS7Cn?M6N>}yx!Y?W>UMtlWN+w7BqThJA^Sfy3iaDps<4Cf;s z=R6V9IXOcsI6cI7sL*F7Bi-9XhAKGIf8I1!m@U1c7-%5o?5TPH?B?s=@hKc_YPZIb zcn!z#?2CDTb$yHk9TNCULHixDIGk@fwu~0YfiXT{5za*|;g=i0uH_-p8_Z&@k8p{{ z5h)^R@MkEX-;ubryF2!WROFE#7GVu2hvO|iZ;19IP*`-P z*A4ZEU>SvP1%1R1+j=syAucL8pp;||MwgJf#vR&Sd_#sV6H?cqW)7FZr2b16wTT`j z>ZE+OQ^XmS%;+QORl5dCN6)e2Y2QUXBj9C#QrXIe(rWxnq>?b-_k+3-UwYK?ls}W(DayI70>q+6=!k z@xQg?nFaaEm;QhS;f?<6!><4@4JZLUT>?FQQb7VGQ=sHLeiihz&w{-2+Y;~p<0TW< zjiX-U$XWX)YO9r;C~MA?++j% z(UcIR49egw5@lb$jo`t5{4s?gASUkJ9g@?(Y6>3h3#ULr%n;kJ9{-0o1PJuE-yLplfrAt8!CY>JH%j`sh< zl7IhUXBae84IO4;BhW#HG>X!7f7ry|iq+2$Kp!_K2acjdp#KQu-w*NrXNV|D_#c4- z&;EXh#Lo~dX(0mP4`F@}M14iojo)GkaMT~R@#}{~ji2Wu;n&Z9x%&=?3?R)Va;rKw z1r6`?pS1EzYa4%;!M{J?0AH$n2Sj+Z4gykz1BQY6V|xG6*yjr(9E<%AA^y6-e|h)Q zIR1IH@95*7|M(9}{!c&z-18qEM+D%Ui?i~Iq+aY`F{!MOX1%HQFCaqyU-0?U#ovTg6aj1eo0k9V_HTmtXU+e?^Z#WKHynRRY&8N~ z1I7@{5AglhpZ}zfFf~M$Dwa|c&Juu>@y9wM8DW%)vlv=|Dkn!#l`nu{h-w!wEF$j@@>r@l=FjfzNLrnzy3ivKPcz>spZ?6 zKPcx1<$OyI-+%psa(+W+*KcyDU0(;KC`}4);N(9*Y zzCM0;+kaf~D@0KKT@W#UMK+AGbFKe^u3C^-0tClG-#1%l#VZH#9Fo$Cr?^F=}gINA>mQGA&$2uK-t z-4dokj4TD2|5Rn3n{;P5PvR@m6`c;Ep+n1?ykvWtZ2HVIT3ZtN+0v9kCEevAjmqvlqp=hV+7~ibY&Z>c#-(?P^ zWQO5TIFc-j^?6Bfyk}JbozD2A4;Kgy0Q%uqlW?R~KJqBjI^;qF&SCnmLvKJJkV#7r zYv43=ewM^kxF`_!h}x%RE-+QkU=17`s#%A6bFTFGQ|2|Yv927`rJR>*Lwav%ZIC+^ z*cv_g&}?;53Y_zt2nJ_84g43!;>JfEtFk#v9;3OHAj@-noUyLmAehibJHLQh!v58Z z{MSn}s6MP7diA7xF;{;bA{m0N<5wC6NN&9#O4vp(259@o7)YxR&g_z1u{)f$N}k7^ zJDfI0vMryMueqoM!Z$3a0i-OZ1_n3q_^|j5)I^NqE7qaweB=&rB0K#!SG~)NrA@xb zejjQ!oj$_&(J_Nt@@pOKUqLJ*@27%O=%R@8K5A;LH(F|v@AKUjf2k)vJ#TTRTr4*3UHCp{%ca<$c`z>XPxUNDsdeKgNx!_+AH2*z zU+bxN%cO<#Y6kf_c<+xO%+IJnKY3ym91G3?Sh_W?+BV@{As5b6-18)_V{#{-oA-jV z?yeg{c_6Cvz70f?O>@Vl-)BhSTEMi5!jn4|$u?~AV@t8N75GO2XSR<`vi6YglY2n( zZ^4Yx1emj1lH@!2F4b$TnK~f9pM<+OYT?H9jwGz9hA*rj=l}0Q;NLZ5P8ocp4H~KU z!q0C|2q$2+og36@*RT$4V9klcPbv5E?yYVhvm2f~s3E!g9?gfhc#=8zQ7sb#Qy~cL zwt+hIF%X!JDzAdPdvvveJXrB1&ot_0IXK&m9|lK07p1@muhFK(Z;ELY&Q*81Zxc%^ zQ=(GX15&YO@yaj@BO}xwwUO}4(qzxlc2fiz6#NFP_|4D8Au+`a5s3t^(e< zdN=DUl%gEE&Zyg&lm`exN5;hVOA4cz6xX3m=Xtj^Rhn07mr;QU);*xj$E`=tcMvu? zcNc?XWk)Iigj6k}Vg{6Rd%N^@=(9n28SI3;9!zW!9LHUX!I4;uWp8594s}*k4}flx^~3wKlm7uXewSC6cpxo z*J!1)rPUgiU~65nBbr^P#^m-9%hs4tDBeEq`1`mrF`AgfWz&Q#ac+phlew`ZUBI~t zlZXV7m&sSDwLD0}>5YXEO@}VK-Fy{{J<}nB5apl}2xg4h=WDbb;W$JN+8rigUtAe& zv}BdJ>RK~zv3Hai)|kw9k#DDKA&Xe}PKMo{rJU;!g7!nhoB-{jo~q@52y;_Or0L;;P3JaZL*nL!WL>AiiJ){77nizcBFwJrzHp3%j{psd7P(6)j8<|3v?A zf@WL5CKl6J-w_Zs;1gMNC_*IkG(|kB9-_$KZG3<5b?%@Iyg$&a&fJ zSbJtxxfS+O20uF1CO(9A3_yxEzfV*cA_uBnR54;0G>U*e+~V;o$vqoXCiL*Ez1+{( z8o{UIq_Cz4Jquyci`a`Pc^5b|glr7rGeGL`<)PU*DndZrtab1$ngWOQ5?$u|JL~ z$8lD@p9KbG!_?1BkwKOra16Yrl=ZFUWqMIID-M{r|vkMfm`3Z$D z@GJ32$)=nRqx>V5H!={ltfNdJ+LAot;bEyENS zAJB37sjzZyVdIoc&6J}>#%1>_QNev(EkzEQ9Zhor*wfZ^AU7Wgt#cN=6pt2CSMS^s z^@={CO;t|OO_O>0czkGlWIGKFU^HBvAl>=IGcxnhqF9geUSe|GVC1KU8cqLEc|9A8 zo;93rg6W_t4K^3i;-9n)bwVHY)Ei!A>Pz2z#_-MFY8y%Q2jypy`&L}yfBAjn?nLF zBUE_U)U}CTs==zT0O+u5z?*hYFI@%%$T>fa&j394WK7)LB|07iH8b;xE>;)$_cIc* z0zi#xiXfRx*it1VgpH)j=S9dGl8S50rP#!pi+~6MAx$ zJ+}(JjXmahU|Y2AtD{ZEAD*|S)+V<+3}R5{CB$viAFn80RqFt;an?9BGgiq4#p@8r z>`^}tQWm?rtgS;b?aRg62)@eFEH?WK8dW76U&cDY3(L4^4H!~GdV*r*!mjS5FcS6( zJY^iGz%67P>5~T{@n}ln zMa)WWqwGU_hUWw?BgTjwhN>;Tp^?o}%R}T1HZzQ;HX8exUsRgWdkN?2XA9UVi*I7f zRzm>^IzV5WrA3<_>QSn_Q<@(o!r6{rlY&>t9^&w%nZlFYeiEUE0j=0_k%n^bm$SqB z7eozb%V96mO0DX7t*`>r`Qarp&tHzVT(}98Yup+q$>&^y#><@TJZN+zMPaK*#%rD} z)N-0#6sm^Kq5V4wO4}}fGFyj^;fbOiApVnB9h`PgxtNf6Z*yfY+yPY?#4Ca{$Y z#yX-}=G3GZ!y~dPulpJn*i6$nzh^vgDMoZ3tCOBFzYZ$*`G*^cdSYVCRCO5CzH@2Z z7;1QCR_Vln%&z(XrH?mX)!M<*m66F3&kKjP)R%E$WADcLng+3}PiNBzv}`-eQrq7^ zMJE%#e`|3%p5*jJ%a|EjBc*TQHaj6(6yJ3AMlp-dy=gfjjx91bWmuI-iC>;O6U=A- zwG0a~RY*u03nHFn-vvsQRMHuiX$7ijm@4jlTw-)r%!vSrLA!k03L`^~if>)UJ>^^5p*(ch-h151 z;!i13(K1_o)TzSY330J|1Futas0aj0w(NPaQ&rm9P{5a3NzBaSbarkP(xuz#S;i%} zOnG~|>a(d2c9{KsxjO_06MGAkwQS2^Sl-hz9XRrRV!Bx9=~8K`eOmbNl@cHk7Ocop zA8OVtx`T-og-kQ}gM5SJB3vEG^hfy*{QRJge|vsd%mxEBRceDBfefRUKtRT&QWAw| ze_lVb-T;VA5W4w`#9ZzD#%A=hyPj3|X26|;T3Y{N!kMgiLO_wkc=+S8B@h4^u=M6r z19Ab}6av<#dKXD&P zV1)e4eJG)X55jhWN=2l*9R~N4;d%l_GZ}NY*1T6{+>CK*2NHs_Dl4;&A!0S%uhZ2u z3Iw5_9J|%*dnT>kCoiSC-b!X#x*c zqB(x8V%8f^LSm&?1fXi4lZ{#O}bt*(UB9%Il!?H3)1Ho+t+nc|B}` zWjCI5@w1rdebc+P^3X^oP-ntMIdE(%1&0H7CYnEBZn!t8dbeJ1wr)}VzyZV_#kaz= zIqr7dNz)p{;6Ae9P(bHfxe}Xr6&3kt8%dc{br7apB9M2U=L6KS-%*?Kq~}LV{%8@w z1t$=-OX}3ad_S?ueY665(;u&zDyQ$7Am_AiX@v4KX~fyv>#qt+YS5QsOlsT5IaKBy z<&LYSRcTkFZ1&Q=8<*i*cvnApayP9hTALMXkLkQf=XvQE53|CpXprLgOjD z&Jo+>vF)n0R-0%hjk1aw($6q)2{@TPP88#2OqAWKEna;zK4r7d2hAr%(y%pgkgV#j z8ubD}`tVH~LuT=LZrPb~5u`O~dAdjDx{XuAxESTFZDS*RvO{bmuSPLu*@{{pqov4s zDHBlW5T&(GxVCxr#LG{itSOOl+#h&9l5TYBZK>3byRI;_M5cLEnIf-CeOV!HD4aJ2 z7#!^vVgrvghY(^YxG4@CAFp%llFkv(J23!k_^=rbeOAo?jlKN-?Rxvfb)yBs;352S z+7T44ifN+OX_dDhn$3s?@a1cbO@z_wua!6~;>V343q35t?$JjL&4e%KppKH)AJ;|pJR!3sNcNm!%j=I z_7yr^T5ihf-s2T;+z}ZR#&__v2eDWS>5EYhjudS`=SJIV)!nHw$bvuAzudfwpp7~8 zDYj9CT7gfQ3U}b+AxcaPn=7x_{3WE~M>=6d`dhKmdP>Zr!eZqsv8TcYd|j$LUGDQxuoVY$FvmS9x&n2*8iV&TcKOe1t$fFNuKx+D;jY^ew= zO(VUc#`x&Y1=J{GHZ676;Pn#{nY;JxY0zi1LeYjs!?Hg14CPUr!e;wo6nugxS`^^- zT!pwQKBg#GmtuBbJ$n;&+~Ax*%=2gy)2rEV|4PMk6cWHOs4&s7wzM)g*Po^JI+n~H+dYyL{k0_Y^yqIPIz&eeHAncUdYNTJpwJ>xxxBTE5(`AtiU7Z^4R zD{ytbdtB(znn|lx5b?O-0^iF##P<2H%ZAz#2EzKI`dt!!vd5cPgn%Od**f~>%_i?{ zz3e0?SH&&(^5x}PqE#*IMc|v4`BVP=)g&I`bvYbbM|iKG~veOqS_QgI_e~Qoy50q1tg-=p+U#l!foRdzen7)0AVrz=4l??(T)LK zGwdbI$sK$>ZN?p)vB4}lv-G^hK=K2(!%tr5&#uRZz+g43F3@u49-x-*Wh)3bme+nN z&0p`H^kmSio(~uSGx!b1*EgecZE`z@`wl_NxJDTli|08lJ4RY(GSNAk1g<-m7oVO2 z=~Mnpiz&r_feAc?ziIYw8T3_}dRhdMEdd%YYz~2mSYpT1wkF8b9&dWOwAKfLYyNtq z$&9B4M`}Ibnv*m4VcNsYPoA#58xNJJ*Gd^ViKWPwA|Qd0 z%`;`^bA7vb*rjKK@v-}9*=E>jaxc_gFf3z`W%gIszU0yOtqWvBp(FEg&`A=;E_2Gw zAJ*guU{)sBWnVZYnn}>x7b59qYAXRn5Mz=YJ3BVz!z+kp?f&bGg_qb=F%D1`&n+bFEgB(p)S(P7Xu2#ZzjlwYyV$N zwZBkh|6L+ztmi(4fs;D3vYaX4nS!-B;bXkZ%tu&!YdCFShMGI=0?Ti2s%nQ3nfocE z_%2`8wt;M;YT_3;L z8lO4vpV}Dz?!f%AKK6wgCj>6B6;WI~v~_cUsj31TpTl|7;ehXYCGP*N^&rS=qrIg<3ZuPz4a-U*yP04QK9ecv7@yiWzoTL3!5+tS3xBB>fqS_`aJh z%%urZ)R8u~%*`^+t0`$ZXl?Rm%x&;GsG_Rvg}S8FT_4&ek%dj8a>I9 zIgxr%pQ1#WwhM9B6;ky3vo_PW9EoE$X1*V5s9t1cN*^EC8_=0Mlx)@HFpiNK-pq_+ z$aGCVt<2j9_Y7hf0~+*wnHk~ zu6A$l=CWh-t+)A`cqjKpFUm`Z%E;G?m`(l4QTv%oB|A#NDj9b$z_WO+m)&_MAPZ?x z9I~nJ*cGrP|30Q5p!Q?^T}Vkq-0VR2@0rqP3-23w`p>e9-%;g{*ZIjD`Mnx-2Vom> zS*>Gl2ELM5G0NAy4q?f~%fSoyR$L+3iexdz1QI7E;SX%D5>6bI0)qXO+t2p<&!hY5 z$`~K{rSdm^il2B$!A+-i=u-gAZ)!WjSm@C@db)>*O}D5hH&OOKP*J*cT*R;D-C1Ll z;45H0nao}kEbkE7f?rees(v!5+`J`jl#{unNDJyrF!4tJN)DyPc#{zppCp zMu@b$)&o&1Z@?6$@ue@X#@SY+wjT`V#t-9H4UhjuJ^ZA&dp0FH$xc%8bxpqh&6brs z&W3d;y3U7{Fd!3pA>|G~M+W)XhHCUHHRX8^Sc6=wru=Pd`+PR9M(apzmlFTfWF@12 z#<ZN`N787NK5=>}B#;!N5LxX5P1iB=n`GIBoT{%B(t%SjgrAW?YdKzyZ5vSeJMZ1sN7kGWFS~^X_WOfl7fBLIja;y>!q|^%k6OF zw%^e}{dryIP*F2!u~!BrR(Ir+P6%5v7+!!qd~HB@s&7N*p&Ng7>|Xs)k|U+K^6BWx zGrn^t17Faag8e9A`Ivn=2%)SZ=1ylUCZ5$$_2BK0v(ZV3pEMmWOWY3*W!aM$C|exD z#o(-XR}XT%W}e4fX~>8{g^1}otLruMCux3qSV3T;PKrjtL|AqD3e|7GSroIn4V?5~ zs^}pPOdC5q6M@Wh&G1==M$LMGh8=mmO5@&bV};~X*>{gzRuw*U5Ta+3@UziCUB=X> zX0jd+q0DJL%@W6#87JYg)3!!G2#OzCW5G(~+p*6j)!kEefVVJAHlRY~hW=kQ_y+oEXJsqxodXdb(JZ+7QFW zu4ZZZay_=KP_;OQBaUYu&(1@QSk~6so15cfB#HhCNPbKJYe+cf9o6k(j8_B~jox`B zF&@FP&A5b7np;#x zrOmo2G$KqEtQ1F&=0?yf1J9<*RZJIq3Kll{*0x3v`C_v$3v?T;B{ZE~T+*uAkLlJ>>fa=fh*FR$p#ccU-T|!hS+MwjPaB=O_hZLz)+DQ^TT|-3z$9u9qH6nm6o0bZ> zaAj`@6ow9T#j8$B+G20JKXF`*)F{2P?#Pn>{aRWGf=1%@TpoYyNkQgqK4yv)els9MV^_`Dodu zx>b5qd5NAnXfFRSq^_m@w7th0H7N6%Ik04C$N$&fcZW5R?|Ua9Kmq|mXaOk+gpQB^ zDhNmtKtiul6p)T!p;0xK%oZlsbMxSjk#klNHe|2Dy) zfo`2yv|5r5M&Ev{NBma&Y+|XfG9&IVxj-NCGJqm9Y%Ow@M<>%gILDSNZ0?ASv8#1Yiq4A>36&)7DnTbX{!4)?P;PQ zSLNb-*0tj^Os%iwsWabb)HE?6S(jMjnB{#acCPKvuEAcoQFYq8FZz9lvm3hP1aR)n zH};{(UNjr|ye&mcni%18S6tyn&>8PiU2#5n-Q37V^^uu(OK zMkWD%S>a<;q~TCck6s=ooC!L(KhS-(s+}&%J1PDA=sFB!G1mFI2USfGHeOzP8v1B2 zv{xbUrle{O+GO7_FZba-u8b@Zp1Ou|pamUfA}VpKb)=WjnEi=wm2AR;Gy^{hyQ&t^ z$Z5P0Svzo(#)$%jkc+K?&tWjB^sWzB>kq^&oxU5%$U_BT1tUTzN2Ho&a^HMjyJ!R# zG8q}ilPP@^$%8C|C)f3ViRDapvd}2ZjTU2SA~0Cp0jF_jh{z$3kuE1^Y2(nInfM3E zbXW67_ac{BTek6?`b7alEeVSzO2J1oW zR=*uk37#H)43ZR;e1o)zVTLD59a7qmlyZ{dkDR|;;<md zko;>p!z@M?o`1cT0t?DkC)zS_N~%O-4idi2#yY@Uzr#xq3_N^aCRB7J7LdZ@ux{yH^Ddk zI0PSgRbdcrF;2>#hfqnzF=StE_CAA-Nw>}#@iwiaUmsY>f=m-!h_`N5UB{&NWFTZ+ zT7lx_w~Rkswme*29iiBm=Na^#Q@!FP2S~1$5@hO#sJ2DYQYXyQ~u3GW)g&Nidy*Pskf|BewFc& zmb)tVX*S{TVpop9pTtGs+~}pU;ngb@VVNPGSxT`MFCT%O(LxYHb<=XgT7z*3QPRg> z^f>+ud>qLo5X*W5I}#<+5eKx zzc*O_lFlC+GyhjQD1R)l{7;&}m*_JoZ|n_^2?U-OZ`%Q#V`*pM1U4=q8zRps@DGa~Glirsy_(2y*#w3X7YPH0REGflC!bwQs2 z@%EMHyZ=Jwm;J{oaB;2U=db;2wXspZK}W{HO4?a?XUEGnT%l+MS!eIj8-y#tsN`cwYsX zN1BZxe(u5!69h-}3}-Ek^K)V~ax@fjd??AdP@V9$I=k4u)dPmJ;OAO##%Shk*{#SX z&KV=Q7ajLcjo=bQQI<^LYIe@Cdka3`1vGj}x}ZrTONp~0W(|#CE|$5LYv6|?V@k+Ke=;G zztX$Gr>(V($rMohx6+j!jv*E#b6aX#aL7r&@E6(+$kEtG#j{kc^ztHD*J&?J?x@N=TxiG@Te(vUexs8)Tjhsd?t)z3g`eYvmwzSl6b&<+$WOSymS^nf zC@)UohAg$};MBQUM^F{7plt86ATHQJ6gYu&5LFL3BkLH^usZv>zr}GPBtHRO9CMhCM9D5cXpIm1; z6ug^=w{Kvc5UyH5SSn*(4d&f3LV{XJ31to;eqe1QoFJn-uS4<-L3zv*NW zEemW-X{;2%emVIV*+d@_O-gLGH71|Ln?f&;%ai&(*pY4dF`+6ar_AG0ygXd4b{Kb{Xdr<4Qx*gp=a95#5Z)orikmEf1sqi~gP4#4!VrE2vzbf5h z2Q;uRWl_c6JeNmLnNgPOg{9<6^Jg9lF>F48k#o*^70x_9L@Rf1&A;p}nPeMkpMdt{ zXhNhZs0yg+k0vILY)|=kYc2nvIH^{vkiB4?%=bm4$snVCOr{V5u6BM}!Xo>kN^93o zj73nIj1snN!Z0Sp4j$9sQ330q{UpS&am+K-afNqH;gG+8FL`=SMo3nCh}RNJB#57R zVRs9Ap4-3M6!HcaS+ZxT@YoG6`PDeMtSlD0OU$AQZ=_6;E|%vt+u?=jM z$!J7pmpzlK5OuUha9+5YSV7QK6WWuOGy=M)!E2I@a-by#87ia2^f{Q3o35|$cHBDm7S? z&4qykInq)^79+`}U(K!X8YtNrl#{$CopNIW=2An>XdcfI&T4!`U+Z{zw-9V5*%xMO zo!uIXY%}6j8|26xIwqCcb=HE=k5DLRn)i9cK-EgZ$g&bn#P3|^Lt@{#}LV2EC_O_~4(8xdU;}_qlqTP;R+(6FG9~;cb zVKKqv5B{=s>S5jHNKk^JAfVK7ecfvOjC@x*D&0raS0#+6Ncf7?5gkzu{i}haR*WoA z5C>_@&L1K~=n|3K6)}+4aS{tvR6zT>9&jRC5SSw9{PzyWHC_d+^(!wXjWC$)*N{rh zmD#s=jE+LN=MQ@gN5kjy32EU4KZkGJG@1k1#bk*!wN5tD`n^R2ZzKtui^l3o7k&w9 zII>4yF!mjnN^}wCj)z>GatN+&VJ=ZkDLqYA+gHLu7|XkadJN*`J`KYx8AUqr~N(aXh$Te4xQ7n=6C_yM%9W7B${-RjkTC zyw6Xxs<)RL+hs*EqaRY#a%3RF%nTn#D8vEOKV^M>tQz~ZNQOqp$PL)kf0B&oX22+;m zmGKc^c^@JP8;?tuo~ashb$7l`-KPDiSM%{t4TS#y;6yp!>S?|$3R*%sF9Hp&&|qo( zh?SW~>Hp&k>gJJuu~$Or#EmE(rD$R)JM*6n*rt9!>CuHE{$XenH1@-lNOs~zTU$GO z>CNlw`8yzeyF&^R;`dH z94BUNDWTp$lG)>z*Zz;tolV0HL*6Zc1$L?rkD(wn3PI`GrG(yEAV(d9HZ0bt(%Ab)$$z@sftD*yU4prZACc z$Rj)RO0ADVIMWcH)*=`~B0=dKVnQB}7QjVg=@wd!24RWCE|MPY+syz(f6fNrL<7Jh z%p3Lr!v{yXmaN$c9U*s25S-IC-~F|$&dDH#1|5U6bj><1kmbu21I{)XGPZ8^w-aPu z{EZIxTT@qtRT{sY2(%>3AyznHL~y9~@lSDD`J8YfcEbvZ;)spS;P*8{C{r}uFlc#i z7v1SD>9Ypw?2F3B9I(i)17`D|`LI0RY4@viA>rpJT{--&_cPMzs_!#nG#iIxL5&!d z_b?Bh>N7Y~>}-vP+y0~rateK|!)uM)cKR>BlQVk>Ww!4|^(%A(td!JHxXXc2diXt# z_}fqpdu+O(XVN_`JLw1V4{TBZr4^==ljt8F$ALPo_viaI8eG*yC5&@|DbMI#AaC1I z{0-eg8_)IR@y4uzC3Da7)@`FngGj8)sRn=etH}eZ2#$NGdnF`SOt;#CMx_0P&MfLbK92?|64$H9LtN3~?B5_JkUz>hU6xWm&d)f!N+Q=+& zhxBN^+H^ct$9n|2*GGVD3$&vg1@hBl5ADdhkZk=`#qM%w^6?1xu6QE#D zt%Ivw{-a@KSq*V&@Ad`s7k4813%;An=@8?+9(3j_9*5w+&AsS1t7FKeA zy^reqZXSCz+u&*Gu9E?aa2s~?4mG%dzW;K<J`O` z3ZGfAnCr_xbB>YtrczGLZn(xYBIs3J@{bGR+L%I&8&O?TVod)x?i2@ zfj*|IdqbR(c0T3PwG=Y!(CPT+reaP{>F=BV_0bqStO95wQzkQ)xkT*4*&84-rrnzoa|v=Xir468Lfe{Tw#B-R{5dn@gowQdORPYlbn`V84A9yCRJviP zLfzB@cnyXI)SK@opYtTjC~AY3IlXQ*LoNiYiNE|b_xkRqZ|zKxd)PI7Ny*qXeWFQ{ z|A5po4}1tA#sbic?0>|Mrn7Cj*xeg|1yqtK?;Ig1na#YTu4}a?T`x$I&W4oDFcr_t zJP`4xPQsM*pA{38kCwczt5+S`W_a6nWm0>{p@}l3>jkd9A?q3&_npNec|AD48J(Y-6BY&e%ZLRpdJ@sNlo%&*RDT1A5u zTV_yLr4V`L8!C2;#%Sz+eDR{reyu-(Q2*|Y07|RP-$nTckQKlynq_HxGz5Z)GlsLQ zS^%8=`JV%$%7yJA>zYN*{Tc_bEb3P3O^>$oUq8I~KVL+R-|`Ej8*6<%IWTBj$FXgRY=a|~IFyI@Z=0POld zVRrsOLf*6!GVCXiShZG3fReS_lVWpSCwgE9gaxor0$-_p)OxnjwsZVD@$`Jcda(Xp z1002|u}%d>urOa@9YC!4(Gc?krUuyC{e*4slA5~pgE{#fU`~Dpn3Hb-=41=NoIDFK zCqDwr$-ma8Znbv~_Duj$382PPR4+iA*L9e;Q1**g_Y>Yw*FB>II^#dNsM#&yF!_RI zUp#QH*;LlgbRx&0Fnno`^Tyr0BzTy%xMcf0z$|wwDeY$sY%H+#sik*2&&G_1dXuxJ zA6W+cQmfHn`Qk)nIm~!Tn!4>P1dNHVO(O`mGVWSxSBY1S9z6S8W0n8J%9r;~JQT!D zQC2{>vl+@aCAQoekiI@$07A9PNH+TpXKO|oE}?Y=o>L#KUz1@cb5f`NULIR@M8J=k zS(t#}vC%+eWLxXS!SM+gdt$6FN7=tyrPTny4?6TYmzC$_azQsW>n%?+JOlbbz6FUX zn=TG8gIT5QP$eKvi?b3iTwYjlRIr%F+M_yDTB4`5eC*M(61fN)22PoLe8fetdQ!tS zY*TmD1=gtP&}$U^_R}=sy9m5%@zAKX>aa+0@UDQPq4^|$2Og_tGVOHB+pS6%@@z$c z!(G>$BA#H28`+4(-2tESdaJWn?#e^&?xn`sZ^yH-ll1p_o_^M-D|U8*lwQqkWpxw;7ocm{{R1 zJTh$%7d)t#KW$7OZ@O-*oOs^MlvCJIKNjsKTNF*Agem4(k^JdN-m=JILw4Fp7(g)q z?4uP>fK57afV%DU>&$T|*QZ2hqkPiYm6u;cS+cd66wS)sDFNP&*E%gTC(3rQFwsuO zm}WYZ!wr393PjvS-D6cszTL{iY2#q|QGZV`e5=2;lIC3q;neq>?E%&B8rP`Al)Bw9 zHosn=MD&*&uYY_gDH|=vt0(C1RhFv@CP@tBC^fR2BpNKP95>-sk1rahEDTcM*1g5>R60l|AJk2`biY3Y zZ12ht#CCXmp^#{|wE0CMe1yLiiCY&-=E6#_UX10RpCr}u#FvRbQLUyJm?Q)E%=G0S zFYD$`k_dzUMT#6r;3oCm4k$L>F|m}Lx;9kB!L>;Q=0_@BKk6vx_1j|yL^*NbgdzI~ zU=<;=j$KC1{IOxxx+R3YKEWycZ!Nv8iO=I*GskBEZ`uhHhV83? z+`iB${?lu%mVh&+K8cuEVY{C%-FHWIO40YET2s}oDt!`IuVqn@R2!pC4w zr1G+3Rxc_VGl)JNlVo!NuBUQN@foml?SZcTn;;!-BwW+O`?enc2NE4@WFmdsW0h0( z4$oks_M&VpPJz0Mm=_=zI85PZq&#Xo6Nnce#E(jP3dZz#H;|A#rcq8P%*)=&d=xFv zS7FP*CZ=Gj@m+eW9Cxq&*o`OU6gl+NS!3M{Ge2A^E>liQosZ-2ITKrTT$YR1Emhj* z+u~JjX;*okxJ$fuXk&416`EfetS!Bk(3HWK7Ni@iq>xKKMIOIr$-l3hHC=V)l`Z@{ zSglQ(H!(ge%6JZhB3K3XK}b*@R_o1^3sHc%!!`r?GNN?lCd+6C^fU5X(e>goRrSWG zh(`9;U~n}h6+XhhH6uAw8PO>dFWIPXu1*Q^zl5sHbGpLm?0WXc&}(IH@;uLGO8Q)A zoa)T%#W`1)cUdS&)$cKX)WXk7neuw4L`OYbcydNLI|Xy zPX6c&sqn)gPanTIk(5!Ap$p-Xz3?K`?41K{8Uj|1=Ll^C(S1X6M$gqe|0aoDOGb7; zrOoz$Z>r2zBNgbmmsBY_QoK5+j?=nm4N$P4YgTk~Z>I&9e0n&xU)AwtOq>zKHJ2-a zB>RA%j_)L!KGit*VRv5qrBvj#oP{p!JY^N$m?b`@?6d__+sQC)sI$R{JNMf0Yv6lg z5fBQ+8W-|FsCGL+54U=JKd1Igj~1p3nX_`S%*7LOINuiIcQrHRFrTd!r@Qf87T^L5 zyVijee=&z=;>?Pc3;->Vw!Av2F2lXe%@*hpeKP&)bB2xmRS|sw(6I}3gWH2T&o-2q zGIH@>_T%VS;VKDlRq1jQ?xb+Ir&L0d%+JBkFy;6tf4CVZa$OmIdxq2>y~vugDf_!!Hk_^^VWxrONZbB$T^mt|?^X!z?C`t^(st{R6s;ttG(#1faSvD`@ZI7-Ev9U#Er9&pe*CCZs);iEd)$ThUjKJS*8#HiV=0U%;=(AeimciU;pydUjO=m z=>(#cg>d-EL zKlSX1Utr^?>+X>N?jM+aE~9-R{Y={)Wk{g3OXp>z9fiotIj6|2IPzATrD!rtA6j&v z3n%jSm1GFDsVL+9)gn(b_bXt%0sZ*FLYsxTp~hnQKp<}uVDMAJ1K(?WJi2x)cCuOk zV)mo;Z@AV*{r6~(jgj-;<=0de4sRsgAA{Dn0$u|Dd( zsi~>hfWLl*lWPnUmS>bj=Pv%8yZ@YhT=|M^Tl<(qQ>RQ6>UCMdePT0{!AQuzzO8RrTIhDT%yU`8h44IaE2 zM_2d~fV%w0W534dKi)_{ocSpcBF_L4ESCPp+JXYlH24$G(afta>e_zt*3oF;@hRf{ z?V)~sugq`HjfyJj&}o05`UGUo^F#L@r!XjZU^CgX7*GI*T?}PXMEMaLS)V;s8 zdoBCI*!d8KT-TX>Je@yjH(ID0N!6VP^0VKo^MinDEH2?9=ZlcrrW5<(0o6I!j*RZV z1k^zG?SQf;*bdYUD}c%KzvBz?$N8-D0T*Qt5N8DTZvr5g$H6-w<#tweacOxwi>vi$ zc{vsMPz`hufR!D=dCS15GbY#^)Qun8{r2U5l~DeB7dfW1V>c}QO7hpoCHKqkI2D&f z3vinr0y;C@0^6|-4Gmw9Za$gsb-ueT-44w6!G`NeV(Mj*las4I-EztA_F&7s)lQ2Ke$mBo|1qavll`(lkH4;Eq@K`04_VR$ z@%d_S9=rPhmOa6~zXQ^Hu>-2Y4F^~o8Cn6Fy#Ulr(tqHqs9Wto2J{wiU+gbH)cT$J z2Gv3^@$b%AT(`ZTm8Dt4ycHFj`e!0eSQKE@bCX?VW0eK^?ySw fS*+l*dJBl5x81;K)Mee2VRLH%0YVXVXZ+s)&zI@T literal 0 HcmV?d00001 diff --git a/apps/docs/static/img/zcp/quickstart-laravel-ai-agent-recipe.png b/apps/docs/static/img/zcp/quickstart-laravel-ai-agent-recipe.png new file mode 100644 index 0000000000000000000000000000000000000000..df9e15849e9c095d1d4661a4ba4fce177e6c1121 GIT binary patch literal 266203 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

9!Yz)KqJKs_8lDi9 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

GZkpbIzq3Fj`#cs)Lm=^aR_h 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

+
+ Product prompt + Build a task board... +
+ + +The important user-facing concepts are the runtime layout, the development inputs ZCP can work from, and the delivery preference. The exact internal labels live in [Workflow terms](/zcp/reference/agent-workflow). + +## 1. Choose the runtime layout {#choose-the-runtime-layout} + +The runtime layout decides where the agent should build app code. You can let ZCP infer it from the project, or you can name the shape in the prompt when it matters. + +| Runtime layout | Use when | What to tell the agent | +| ------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------- | +| **Dev** | You want one mutable runtime for fast iteration, experiments, or early app work. | `Build a small Node.js API on dev.` | +| **Dev + stage** | You want a development runtime plus a separate runtime for review or promotion rehearsal. | `Build a Node.js API with PostgreSQL on dev+stage.` | +| **Stage / linked target** | You work from local files or a single target runtime and want ZCP to use that target. | `Use appstage as the deploy target for this local app.` | + +The layout is about app runtimes, not where ZCP itself runs. Remote setup can use any of these layouts from the `zcp@1` workspace. Local setup usually works from local files into a linked stage or app runtime. + +Managed services such as PostgreSQL, Valkey, queues, search, storage, or mail are dependencies. ZCP gives the agent their state and wiring patterns, but app code deploys to runtime services. + +## 2. Develop with live project context {#develop-with-live-project-context} + +This is the core value of ZCP during development: the agent does not work from a stale prompt or copied log snippet. It gets current project state, Zerops-specific knowledge, and workflow instructions scoped to the project in front of it. + +| What the agent gets from ZCP | Why it matters during development | +| ---------------------------- | ------------------------------------------------------------------------------------------------------- | +| Live project state | Services, runtime layout, env-var keys, Zerops references, events, deploys, logs, and saved work state. | +| Zerops knowledge | Runtime and managed-service wiring, `zerops.yaml`, env refs, ports, public access, and deploy behavior. | +| Project-scoped tools | Operations for service setup, env vars, deploys, logs, lifecycle, verification, and recovery. | +| Workflow instructions | When to use existing services, when to create missing ones, when to read evidence, and when to ask. | + +That means you usually do not paste an infrastructure inventory, env wiring plan, or log summary into the chat. A short product prompt can be enough because the agent can ask ZCP what exists now. + +What you control here is the product request, stack preference, acceptance criteria, and any instruction to inspect or continue existing work. ZCP handles the deploy, verification, evidence reading, and recovery loop behind that request. + +| Development input | Use when | What to tell the agent | +| ----------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------- | +| **Product behavior** | The feature or app outcome matters more than the exact stack. | `Build a task board where tasks stay saved after refresh.` | +| **Stack preference** | You want a specific runtime, framework, or managed service. | `Use Laravel and the existing PostgreSQL service.` | +| **Acceptance criteria** | A specific behavior must be true before the task is done. | `A user can create a task, refresh the page, and still see it.` | +| **Existing work** | A previous ZCP session stopped or the project already exists. | `Read ZCP status first, then continue the interrupted task board work.` | + +## 3. Choose delivery preference {#choose-delivery-after-proof} + +Delivery preference controls how ZCP should finish work and how future ZCP sessions should ship changes. You can include it in the original prompt or set it later. + +| Delivery preference | What it means | What to tell the agent | +| ---------------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| **Keep direct deploy** | ZCP keeps deploying directly to the target runtime for fast dev/stage iteration. | `Keep direct deploy for now.` | +| **Push to git** | The agent commits and pushes working changes to the configured repository. | `When the app works, push changes to git@github.com:my-org/task-board.git.` | +| **CI / handoff** | A repository integration, GitHub Actions workflow, release process, or human owns the next deploy. | `Set up GitHub Actions delivery for future deploys after the app works.` | + +ZCP records the delivery choice so later work can follow the same path without re-explaining the handoff every time. Git push credentials, CI secrets, and production credentials are separate from `ZCP_API_KEY`; see [Tokens and credentials](/zcp/security/tokens-and-project-access). + +Production should still be a separate Zerops project. ZCP can prepare the handoff from dev or stage, but production promotion belongs to your release path; see [Production boundary](/zcp/security/production-policy). ## What the final answer should contain For a completed app task, the agent should report: -- which runtime service changed, -- which deploy passed, -- which URL, endpoint, or UI state proved the requested behavior, -- any env vars, managed services, or delivery settings touched, -- the delivery choice if one was set. +- the runtime service it changed, +- the deploy or verification target, +- the URL, endpoint, UI state, job result, or stored data that proves the requested behavior, +- managed services, env vars, or delivery settings it touched, +- the delivery preference that now applies. + +If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the decision or credential needed from you. -If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the next decision needed from you. +Advanced reuse path: [Package a running service](/zcp/workflows/package-running-service) turns a verified runtime into a re-importable bundle. It is not the normal lifecycle for the next app change. diff --git a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx index a5f97b8b0..40b6ca4cd 100644 --- a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx +++ b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx @@ -1,67 +1,10 @@ --- title: 'Set up app services' -description: 'How ZCP helps the agent reuse existing app services or create missing runtimes and managed dependencies before code work starts.' +description: 'Infrastructure setup is now covered in Build with ZCP.' --- -When an app prompt needs infrastructure, ZCP should discover what already exists before creating anything. This usually happens because you asked for a product outcome, not because you explicitly asked for runtimes, databases, queues, or caches. +Infrastructure setup is part of the normal ZCP development lifecycle. The current guide lives in [Build with ZCP → Choose the runtime layout](/zcp/workflows/build-with-zcp#choose-the-runtime-layout). -For example, `Build a task board where tasks stay saved after refresh.` may require an app runtime and database. `Use the existing Laravel services` tells the agent to reuse what is already there instead of creating duplicates. +That section explains how ZCP helps the agent choose between dev, dev+stage, or a stage/linked target layout, reuse existing services, create missing runtimes or managed services, and move into app work once the project shape is clear. -Behind that prompt, the agent should answer four practical questions: - -- Which runtime service will hold app code? -- Which managed services does the app depend on? -- Do those services already exist, or should the agent create them? -- Are they running and visible to ZCP? - -This part is infrastructure setup. App files, `zerops.yaml`, deploys, and behavior checks belong to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). - -## Starting situations - -| Situation | What should happen | -| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -| Runtime services already exist | Use them when they fit. Identify runtimes and managed dependencies before changing code. | -| Project has only the `zcp` service | Treat it as empty from the app point of view. Create or import the requested service layout. | -| Project is empty and request matches a known stack | A recipe may be a good starting base, but the starter still has to be changed into the requested app. | -| Project is empty and request needs a custom layout | Create a custom service plan before app work starts. | -| Setup was interrupted | Resume from current ZCP status instead of starting over. | - -The `zcp` service is the ZCP workspace and control plane. It is not the application runtime target. - -## Prompt examples - -Product prompts can imply service setup. Add service details only when they change what should be built or reused: - -```text -Build a Node.js API with PostgreSQL. Use the existing dev+stage pair if this project has one. -``` - -```text -Use the existing Laravel services and build a small notes app. -``` - -```text -Add saved tasks to this task board. -``` - -The agent should explain whether it used existing services or created new ones, then move into app work when the runtime and dependencies are known. - -## Done state - -Service setup is done when: - -- the app runtime is identified, -- required managed services are identified, -- any new services are visible to ZCP, -- the agent can start app code, `zerops.yaml`, deploy, and verification work. - -When files, framework setup, deploys, logs, or behavior checks appear, the work has moved to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). - -## Correction signals - -| Symptom | What to do | -| ------------------------------------------- | --------------------------------------------------------------------- | -| Agent targets `zcp` as the app | Correct the target to the runtime service. | -| Agent recreates existing runtimes | Ask it to inspect current services and use what exists. | -| Agent writes app files during service setup | Stop and finish infrastructure first, or move explicitly to app work. | -| Service creation fails | Treat it as a hard stop until the failure is understood. | +For exact route names and workflow vocabulary, use [Workflow terms](/zcp/reference/agent-workflow). diff --git a/apps/docs/content/zcp/workflows/delivery-handoff.mdx b/apps/docs/content/zcp/workflows/delivery-handoff.mdx index 6fe011f4a..36a32e5ad 100644 --- a/apps/docs/content/zcp/workflows/delivery-handoff.mdx +++ b/apps/docs/content/zcp/workflows/delivery-handoff.mdx @@ -1,104 +1,10 @@ --- title: 'Choose how finished work ships' -description: 'After a verified deploy, choose whether future changes use direct deploy, git-push, or an external handoff.' +description: 'Delivery preference is now covered in Build with ZCP.' --- -Delivery is decided after the app works. You can include delivery intent in the original product prompt, but ZCP applies it only after a verified deploy exists. The first deploy uses ZCP's direct deploy path; the delivery choice records what should happen to the finished work and future changes. +Delivery preference is part of the normal ZCP development lifecycle. The current guide lives in [Build with ZCP → Choose delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). -If you do not have a repository-driven release process yet, keep direct deploy for now. Git-push and external handoff become useful when source control, review, CI, or a separate release owner matters. +That section explains the three user-facing choices: keep direct deploy, push verified work to git, or hand off to CI/release tooling. ZCP records the delivery choice so later work can follow the same path without re-explaining the handoff every time. -ZCP is for development and staging projects. Production should receive promoted work through your CI or release process; see [Production boundary](/zcp/security/production-policy). - -## Three delivery choices - -| Choice | Use when | What ZCP does next | -| ------------------ | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| Keep direct deploy | Fast iteration, solo projects, demos, agent-owned dev/stage work. | Future changes keep deploying through ZCP directly. | -| Push to git | You want commits, review, repository history, or repo-driven builds. | The agent commits and pushes to a configured remote, then observes and verifies the resulting build when one is tracked. | -| External handoff | CI, release process, or a human owns delivery from here. | ZCP records evidence but does not initiate the next deploy on its own. | - -You can ask in plain language: - -```text -After the app verifies, set up git-push delivery to git@github.com:my-org/task-dashboard.git. -``` - -```text -Keep ZCP deploying directly for now. -``` - -```text -My CI takes over after this verified deploy. -``` - -If you see internal mode labels in agent output or reference pages, they map to the same three choices: - -| User choice | Internal label | -| ------------------ | -------------- | -| Keep direct deploy | `auto` | -| Push to git | `git-push` | -| External handoff | `manual` | - -## Direct deploy - -Direct deploy is the path ZCP already used to produce the first verified result. No extra setup is needed. - -Pick this when the project is still exploratory, the agent's deploys are the canonical dev/stage path, or you do not need repository-driven delivery yet. - -Direct deploy is not a shortcut around verification. The next session still needs deploy plus reachability plus requested behavior before it reports completion. - -## Git-push delivery - -Git-push needs committed code, a remote URL, and credentials that can push. It records where future source changes should go; it does not make the first functional deploy happen through git. - -Remote and local setup differ: - -- Remote setup uses credentials configured inside the `zcp` service, commonly a fine-grained token exposed as `GIT_TOKEN`. -- Local setup uses your local git working directory and git credential helper. - -Local setup does not initialize git for you. If the directory is not a git repository yet, initialize it and make a first commit before asking for git-push delivery. - -Remote setup can prepare repository state during service setup, but git-push still needs at least one commit covering the work, a remote URL, and push credentials. - -## What fires after a push - -Git-push capability and build integration are separate. A push may trigger one of three outcomes: - -| Outcome | Meaning | -| ---------------------------------- | ----------------------------------------------------------------------------------------------- | -| Nothing tracked by ZCP | Your repository may still have its own hooks or CI, but ZCP did not set up or observe one. | -| Zerops dashboard build integration | Zerops pulls from the repository and runs the build/deploy pipeline from dashboard integration. | -| GitHub Actions workflow | A workflow checks out the repo and deploys to Zerops with `zcli`. | - -The GitHub Actions path needs a Zerops API token stored as a repository secret named `ZEROPS_TOKEN`. It is not a GitHub token. The project-scoped token already used by `ZCP_API_KEY` can be reused if your team wants one rotation surface. - -A push is not proof that the app works. If a webhook or GitHub Actions build runs from the push, the agent should observe the build result and verify the app afterward. - -## External handoff - -External handoff means a human or external system owns delivery after the verified ZCP result. ZCP can still read state, verify, and record deploy evidence when asked, but it does not initiate future deploys on its own. - -Pick this when the next step is a release review, a non-ZCP CI pipeline, a production promotion process, or a human decision that should not be hidden inside the agent loop. - -In this mode, ending the session is deliberate: the agent reports what is verified, what is handed off, and what the external owner must do next. - -## Change later - -Delivery choice is not permanent. You can move from direct deploy to git-push later, add a dashboard or GitHub Actions build integration later, or switch to manual handoff for a release that needs human control. - -Git-push capability can also coexist with direct deploy. A configured remote does not force every future task to close through git. - -## Gotchas - -1. **First verified deploy is direct.** Setting up git-push early does not redirect the deploy that creates the first verified running result. -2. **Delivery choice is not a hidden deploy button.** It records the future path after the app already works. -3. **External handoff is not a deploy by itself.** It means a human or external system owns the next delivery action. -4. **GitHub Actions uses `ZEROPS_TOKEN`.** That secret is a Zerops API token, not your GitHub PAT. -5. **"No tracked build integration" does not mean no build will fire.** It means ZCP did not set one up or observe it. - -## Next steps - -- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) -- [Tokens and credentials](/zcp/security/tokens-and-project-access) -- [GitHub integration](/references/github-integration) -- [CI/CD with Zerops](/guides/ci-cd) +For exact internal labels, use [Workflow terms](/zcp/reference/agent-workflow#delivery-modes). diff --git a/apps/docs/content/zcp/workflows/package-running-service.mdx b/apps/docs/content/zcp/workflows/package-running-service.mdx index f1ab90acd..2783424bb 100644 --- a/apps/docs/content/zcp/workflows/package-running-service.mdx +++ b/apps/docs/content/zcp/workflows/package-running-service.mdx @@ -117,7 +117,7 @@ A few things worth noticing: - 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). -Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen handoff path expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through the normal [handoff path](/zcp/workflows/delivery-handoff). +Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen delivery preference expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through the normal [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). ## What the bundle does not include @@ -128,14 +128,14 @@ Packaging can be part of a handoff intent. The agent should produce the files, p | 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 +## Packaging checks -1. **Packaging is not the delivery path by itself.** It produces the bundle. Commit and push happen through your chosen [handoff path](/zcp/workflows/delivery-handoff). +1. **Packaging is not the delivery path by itself.** It produces the bundle. Commit and push happen through your chosen [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). 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. -- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) - the verified runtime this packages. +- [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) - choose how the bundle's two files ship. +- [How ZCP works](/zcp/concept/how-it-works#verification-has-two-layers) - the verified runtime this packages. - [How ZCP works](/zcp/concept/how-it-works) - why packaging stays stateless. diff --git a/apps/docs/sidebars.js b/apps/docs/sidebars.js index 3aaa3a7a9..057166176 100644 --- a/apps/docs/sidebars.js +++ b/apps/docs/sidebars.js @@ -636,33 +636,13 @@ module.exports = { ], }, { - type: 'category', + type: 'doc', + id: 'zcp/workflows/build-with-zcp', label: 'Build with ZCP', - link: { - type: 'doc', - id: 'zcp/workflows/build-with-zcp', - }, customProps: { sidebar_icon: 'circle-stack', }, className: 'homepage-sidebar-item', - items: [ - { - type: 'doc', - id: 'zcp/workflows/create-or-adopt-services', - label: 'Service setup behind the prompt', - }, - { - type: 'doc', - id: 'zcp/workflows/build-and-verify-app', - label: 'Deploy, verify, and fix', - }, - { - type: 'doc', - id: 'zcp/workflows/delivery-handoff', - label: 'Choose how finished work ships', - }, - ], }, { type: 'category', diff --git a/apps/docs/src/css/custom.css b/apps/docs/src/css/custom.css index dbcccde39..47de71cdf 100644 --- a/apps/docs/src/css/custom.css +++ b/apps/docs/src/css/custom.css @@ -53,7 +53,10 @@ @apply cursor-default; } - .sidebar-group-headline > .menu__list-item-collapsible > .menu__link:not([href]) > .menu__link:hover, + .sidebar-group-headline + > .menu__list-item-collapsible + > .menu__link:not([href]) + > .menu__link:hover, .sidebar-group-headline .menu__list-item-collapsible:hover { @apply !bg-transparent; } @@ -178,22 +181,22 @@ html[data-theme='dark'] { } .height-100 { - height: 100%; + height: 100%; } .sidebar-group-headline > .menu__list-item-collapsible > .menu__link { - padding: 0px !important; - color: var(--ifm-menu-color); + padding: 0px !important; + color: var(--ifm-menu-color); } .theme-doc-sidebar-item-category-level-1.sidebar-group-headline { - margin-bottom: 6px; - margin-top: 4px !important; + margin-bottom: 6px; + margin-top: 4px !important; } .sidebar-group-headline > .menu__list-item-collapsible { - cursor: default; - color: #fff; + cursor: default; + color: #fff; } html[data-theme='dark'] .docsearch-btn { @@ -243,19 +246,236 @@ html[data-theme='dark'] .docsearch-btn:hover { @media (min-width: 768px) { 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 { + 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-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 { + 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; } } + +.zcp-lifecycle-diagram { + margin: 1.5rem 0 1.75rem; +} + +.zcp-lifecycle-prompt { + align-items: center; + background: #f7fbff; + border: 1px solid #2d72d9; + border-radius: 8px; + color: #172033; + display: flex; + flex-direction: column; + gap: 0.35rem; + margin: 0 auto; + max-width: 22rem; + padding: 0.7rem 0.9rem; + text-align: center; +} + +.zcp-lifecycle-prompt span { + font-size: 0.76rem; + font-weight: 650; + line-height: 1.1; + text-transform: uppercase; +} + +.zcp-lifecycle-prompt code { + background: #eaf2ff; + border: 1px solid rgba(45, 114, 217, 0.22); + border-radius: 6px; + color: #172033; + display: block; + font-size: 0.78rem; + line-height: 1.25; + max-width: 100%; + overflow-wrap: anywhere; + padding: 0.32rem 0.45rem; + white-space: normal; +} + +.zcp-lifecycle-down { + background: #6b7280; + height: 1.35rem; + margin: 0 auto; + position: relative; + width: 2px; +} + +.zcp-lifecycle-down::after { + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 7px solid #6b7280; + bottom: -6px; + content: ''; + left: 50%; + position: absolute; + transform: translateX(-50%); +} + +.zcp-lifecycle-flow { + display: grid; + gap: 1rem; + grid-template-columns: 1fr; + margin-top: 0.8rem; +} + +.zcp-lifecycle-stage { + background: #ffffff; + border: 1px solid #d7dce5; + border-radius: 8px; + color: #172033; + padding: 0.95rem; + position: relative; +} + +.zcp-lifecycle-stage::after { + color: #6b7280; + content: '↓'; + font-size: 1.2rem; + font-weight: 700; + left: 50%; + line-height: 1; + position: absolute; + top: calc(100% + 0.08rem); + transform: translateX(-50%); +} + +.zcp-lifecycle-stage:last-child::after { + content: none; +} + +.zcp-lifecycle-stage h3 { + font-size: 0.95rem; + line-height: 1.25; + margin: 0; +} + +.zcp-lifecycle-stage p { + color: #4b5563; + font-size: 0.84rem; + line-height: 1.45; + margin: 0.45rem 0 0.7rem; +} + +.zcp-lifecycle-stage ul { + display: grid; + gap: 0.42rem; + list-style: none; + margin: 0; + padding: 0; +} + +.zcp-lifecycle-stage li { + border: 1px solid currentColor; + border-radius: 6px; + font-size: 0.78rem; + line-height: 1.25; + padding: 0.38rem 0.48rem; +} + +.zcp-lifecycle-stage--infra { + background: #f4fbf6; + border-color: rgba(50, 132, 90, 0.35); +} + +.zcp-lifecycle-stage--infra li { + color: #236343; +} + +.zcp-lifecycle-stage--dev { + background: #fffaf0; + border-color: rgba(196, 127, 23, 0.35); +} + +.zcp-lifecycle-stage--dev li { + color: #9a5d0d; +} + +.zcp-lifecycle-stage--delivery { + background: #f7f5ff; + border-color: rgba(113, 87, 217, 0.35); +} + +.zcp-lifecycle-stage--delivery li { + color: #5b45b0; +} + +html[data-theme='dark'] .zcp-lifecycle-prompt { + background: #132033; + color: #f8fafc; +} + +html[data-theme='dark'] .zcp-lifecycle-prompt code { + background: #17263d; + color: #dbeafe; +} + +html[data-theme='dark'] .zcp-lifecycle-stage { + color: #f8fafc; +} + +html[data-theme='dark'] .zcp-lifecycle-stage p { + color: #cbd5e1; +} + +html[data-theme='dark'] .zcp-lifecycle-stage--infra { + background: rgba(20, 83, 45, 0.22); +} + +html[data-theme='dark'] .zcp-lifecycle-stage--dev { + background: rgba(120, 53, 15, 0.2); +} + +html[data-theme='dark'] .zcp-lifecycle-stage--delivery { + background: rgba(76, 29, 149, 0.22); +} + +@media (min-width: 768px) { + .zcp-lifecycle-prompt { + margin-left: 0; + margin-right: auto; + max-width: none; + width: calc(33.333% - 0.9rem); + } + + .zcp-lifecycle-down { + margin-left: calc(16.666% - 0.45rem); + margin-right: 0; + } + + .zcp-lifecycle-flow { + gap: 1.35rem; + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .zcp-lifecycle-stage::after { + content: '→'; + left: calc(100% + 0.3rem); + top: 50%; + transform: translateY(-50%); + } +} From 2ae61b4a9a6ab0646368faaf50ce23655710b161 Mon Sep 17 00:00:00 2001 From: krls2020 Date: Sun, 10 May 2026 17:57:57 +0200 Subject: [PATCH 08/24] Refine ZCP setup guide hierarchy --- apps/docs/content/features/coding-agents.mdx | 2 +- apps/docs/content/zcp/overview.mdx | 2 +- .../content/zcp/setup/choose-workspace.mdx | 158 +++++++++++------- .../content/zcp/setup/hosted-workspace.mdx | 115 ++++++------- .../content/zcp/setup/local-agent-bridge.mdx | 66 ++++---- apps/docs/sidebars.js | 4 +- apps/docs/src/css/custom.css | 57 +++++++ 7 files changed, 238 insertions(+), 166 deletions(-) diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx index f3e1c9468..6ff5ba8b0 100644 --- a/apps/docs/content/features/coding-agents.mdx +++ b/apps/docs/content/features/coding-agents.mdx @@ -63,7 +63,7 @@ Remote setup starts with a `zcp@1` Zerops service. Two dashboard options decide Without **Include Coding Agent**, the service can still exist, but there is no preconfigured agent waiting inside it. Without **Cloud IDE**, there is no browser editor. With both enabled, you get a one-click agent + IDE setup inside the project. -Both options are configured when you provision the service: [Use remote ZCP workspace -> Configure the ZCP service](/zcp/setup/hosted-workspace#configure-the-zcp-service). +Both options are configured when you provision the service: [Use remote ZCP workspace](/zcp/setup/hosted-workspace#what-you-get). Remote setup is still your workspace. You can install another agent CLI, add private MCP servers or helper processes, edit `CLAUDE.md` or other agent instruction files, and keep team tooling in the service. ZCP supplies the project-scoped control plane, operations, and workflow evidence; it does not lock the service to one agent or one set of tools. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index 0ca0b2125..e80dc9f39 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -67,7 +67,7 @@ First-read path: | If you want to | Read | | ----------------------------------------- | ----------------------------------------------------------- | -| Try the shortest first run | [Quickstart](/zcp/quickstart) | +| Try the guided hands-on path | [Quickstart](/zcp/quickstart) | | Understand what happens behind the prompt | [How ZCP works](/zcp/concept/how-it-works) | | Choose remote or local setup | [Choose remote or local setup](/zcp/setup/choose-workspace) | | Start building after setup | [Build with ZCP](/zcp/workflows/build-with-zcp) | diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index 35ef1740c..a77766d5a 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -1,91 +1,133 @@ --- title: 'Choose remote or local setup' -description: 'Choose remote setup inside Zerops or local setup in your own editor/CLI.' +description: 'Decide whether the ZCP workspace should run inside Zerops or on your machine.' --- -The same `zcp` binary runs in both setup paths. The ZCP model stays the same: one Zerops project, live project state, project-scoped operations, deploy and verify evidence, and proof or blocker at the end. +Pick remote setup unless the agent specifically needs to run next to local files, local data, local tools, or a local-only agent client. -The choice is where the agent and filesystem live. Most practical differences follow from that: remote work happens in a Zerops workspace that can reach or mount runtime services; local work happens from files and data on your machine, with a Zerops runtime linked when deploy and verify are needed. +Both paths use the same `zcp` binary and the same ZCP model: one Zerops project, live project state, project-scoped operations, and an agent that can finish with proof or a blocker. The choice is where the agent workspace lives. -Remote setup is the default path for first use. The agent runs inside a `zcp@1` service in the Zerops project, not on your laptop. That isolation is the main safety advantage: the agent can be given broader command permissions inside a clean project workspace because the surrounding machine is a Zerops service, not your personal workstation. Remote setup also avoids local install, local MCP configuration, and laptop VPN for the first run. With **Include Coding Agent** and **Cloud IDE**, the project gets a preconfigured workspace with Claude Code and browser VS Code. You can also reach remote services from desktop editors that support Remote SSH or similar workflows; the browser IDE is the fastest default, not the only editor path. +:::tip Recommended default +Use **remote setup** when you want a project-contained agent workspace. It gives you clean isolation inside the Zerops project, no local install, project-private networking, and a preconfigured Claude Code + browser VS Code environment when enabled. +::: -Local setup is for teams that deliberately want the agent running next to local files and local data. Use it when your normal editor, terminal, local tools, local git credentials, lower-latency feedback, or an agent client that remote setup does not cover should stay in charge. It is often a preference and ergonomics choice: some teams are simply faster in their established local environment. It can also reduce the need to keep a remote dev runtime running during inner-loop work, although runtime costs are usually small and a Zerops runtime is still needed when the agent should deploy and verify on Zerops. The tradeoff is safety: ZCP remains project-scoped, but the local agent client runs as your local user. +## The short version + +
+ +
+

Local setup

+

+ The agent runs from your machine. Use it when local files, local data, + your desktop editor, local git credentials, or a specific local agent + client should stay in charge. +

+
    +
  • Install zcp locally.
  • +
  • Run zcp init in the working directory.
  • +
  • Add a project-scoped token.
  • +
  • Use zcli vpn up for private service access.
  • +
+ Set up local ZCP → +
+
+ +## Decide by what should stay close to the agent + +
+
+

Use remote when you want...

+
    +
  • A project-contained workspace.
  • +
  • Clean project-contained isolation.
  • +
  • Bundled Claude Code and Cloud IDE.
  • +
  • Private service access without laptop VPN.
  • +
  • A remote editor path over SSH when browser VS Code is not enough.
  • +
+
+
+

Use local when you want...

+
    +
  • Your local editor and terminal to stay in charge.
  • +
  • Local files, fixtures, or data next to the agent.
  • +
  • Your existing local git credentials and helper tools.
  • +
  • An agent client or workflow the remote workspace does not cover.
  • +
  • Local agent permissions you explicitly control.
  • +
+
+
+ +Remote setup usually costs one extra workspace service. Local setup avoids that workspace service, but deploy targets and managed services still cost while running. For most teams, the decision should be about where the agent should safely and ergonomically work, not about the small workspace cost. :::info Public preview -Remote setup and local setup are both public preview. Local setup has more moving parts and is more likely to change: binary install path, `.mcp.json`, and `zcp init` artifacts may move between releases. +Remote setup and local setup are both public preview. Local setup has more moving parts and may change faster: binary install path, `.mcp.json`, and `zcp init` artifacts are still settling between releases. ::: -## Remote vs local at a glance - -| | Remote setup | Local setup | -| ------------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- | -| Where `zcp` runs | Inside the Zerops project in a `zcp@1` service | On your machine as the `zcp` binary | -| Where the agent runs | In the `zcp` service when **Include Coding Agent** is enabled | In your local editor or CLI agent | -| Default agent workspace | Browser VS Code plus Claude Code when enabled; desktop remote editors can connect over SSH | Your existing editor, terminal, and agent client | -| Filesystem the agent sees | Workspace files and mounted runtime files | Source files on your machine | -| Private service access | Project-private network, no laptop VPN for the workspace | `zcli vpn up ` from your machine | -| Deploy source | Remote workspace or mounted runtime source | Your local project directory | -| Git credentials | Credentials configured in the `zcp` service | Your local git credentials | -| ZCP token storage | Injected into the service by Zerops | `.mcp.json` env block in the project directory | -| Agent credentials | Entered into the bundled or installed agent in the service | Entered into your local agent client | -| Permission posture | Broader agent permissions stay contained in the project service | Agent permissions affect your local machine | -| Cost profile | Runs an extra project workspace service | No remote workspace service; deploy targets still cost while running | -| Safety profile | Project-contained by design | ZCP is project-scoped, but the agent client runs as your user | - -## Starting points - -Remote setup starts from the Zerops dashboard: - -- **Recipe with AI Agent environment.** Fastest way to try ZCP. The recipe creates app services plus the `zcp@1` workspace, Claude Code, and Cloud IDE together. -- **New project with ZCP enabled.** Start from a blank or custom project and toggle **Add Zerops Control Plane (ZCP) service** while creating it. -- **Existing project.** Add the `zcp` service next to services that already exist. +## What changes between the paths -Local setup starts from a directory on your machine. That can be: +| | Remote setup | Local setup | +| ---------------------- | -------------------------------------------------------------- | -------------------------------------------- | +| `zcp` process | Runs in the Zerops project in a `zcp@1` service. | Runs on your machine. | +| Agent process | Runs in the project workspace when bundled or installed there. | Runs in your local editor or CLI. | +| Files the agent edits | Workspace files and mounted runtime files. | Files on your machine. | +| Private service access | Project-private network from inside Zerops. | Zerops VPN from your machine. | +| Token storage | Platform-injected into the workspace service. | `.mcp.json` in the local project directory. | +| Git credentials | Configured inside the workspace service. | Your local git credentials. | +| Safety posture | Agent shell permissions are contained in the project service. | Agent shell permissions affect your machine. | -- **Empty local directory.** Start with no app code yet; the agent can create the app shape and ask ZCP for the needed Zerops services. -- **Existing project directory.** Run `zcp init` beside code, tools, data, and git credentials you already use locally. -- **Recipe prepared for local setup.** When a recipe offers a local-oriented environment, use it to create the Zerops services and wiring, then run the agent locally from the directory that should own app work. +## Starting points -All local paths converge on the same setup: install `zcp`, run `zcp init`, add a project-scoped token, bring up VPN when private services are needed, and launch your local agent from that directory. +Remote setup can start from: -The details live in the setup pages: +- **Recipe with AI Agent environment.** A guided path for a known stack. A recipe creates app services, managed services, and the `zcp@1` workspace together. +- **New project with ZCP enabled.** Start from a blank or custom project and add ZCP during project creation. +- **Existing project.** Add the `zcp` workspace next to services that already exist. -- [Use remote ZCP workspace](/zcp/setup/hosted-workspace) -- [Set up local ZCP](/zcp/setup/local-agent-bridge) +Local setup can start from: -## Runtime layout still matters +- **Empty local directory.** The agent creates the app shape from a product request and uses ZCP to select or create Zerops services. +- **Existing project directory.** The app code, editor setup, local data, and git credentials already live on your machine. +- **Recipe prepared for local setup.** The recipe creates the Zerops service baseline, while the agent and files stay local. -Setup choice is not the same thing as app runtime layout. +## Runtime layout is separate -Remote or local only answers where the agent and `zcp` process run. The project can still have one app runtime, a dev+stage pair, a single stage target for local files, or no linked runtime yet. +Remote or local only answers where the agent and `zcp` process run. It does not decide the app runtime layout. -Common shapes: +The project can still use: -| Shape | What it means | Typical names | -| -------------------------- | ------------------------------------------------------------------------------------ | ---------------------------- | -| Dev + stage | The agent iterates on dev, then proves the result on a separate stage runtime. | `appdev` + `appstage` | -| One mutable dev runtime | Fastest shape for experiments and early builds. | `appdev` | -| One app runtime | A small project with no dev/stage split. | `app` | -| Local files + stage target | Files on your machine are the source; a named Zerops runtime receives deploys. | local directory + `appstage` | -| Local files only | ZCP can inspect and generate env snapshots, but deploy needs a linked runtime later. | local directory | +- one mutable dev runtime, +- a dev + stage pair, +- a single app runtime, +- local files linked to a stage target. -Stage is a review or promotion target, not production. Production should live in a separate Zerops project; see [Production boundary](/zcp/security/production-policy). +That choice belongs to normal app work: [Build with ZCP](/zcp/workflows/build-with-zcp#choose-the-runtime-layout). -The stage hostname is explicit. The agent should read project state and use confirmed hostnames, not invent `appstage` from `appdev`. +Stage is not production. Keep production in a separate Zerops project and promote work through your release path; see [Production boundary](/zcp/security/production-policy). ## Switching later -You can change setup path later without changing the project boundary. - -- Start remote for evaluation, then move to local when you want local files, local tools, or lower-latency editing. -- Start local for a repo-first project, then add remote setup for a project-contained browser workspace. -- Keep both paths pointed at the same development project only when your team understands which source tree is authoritative for deploys. +You can change setup path later without changing the ZCP project boundary. -Avoid using either path against production. Promotion should be a separate CI or release step into a separate production project. +- Start remote for evaluation, then move local when local files or tools should own the workflow. +- Start local for a repo-first project, then add remote setup when you want a project-contained agent workspace. +- Keep both paths only when your team understands which source tree is authoritative for deploys. ## Next steps - [Use remote ZCP workspace](/zcp/setup/hosted-workspace) - [Set up local ZCP](/zcp/setup/local-agent-bridge) - [Trust model](/zcp/security/trust-model) -- [Production boundary](/zcp/security/production-policy) diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index 24ae87cf1..68530c175 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -1,112 +1,101 @@ --- title: 'Use remote ZCP workspace' -description: 'Open or add the project-contained zcp@1 workspace with Claude Code, browser VS Code, and project-scoped ZCP access.' +description: 'Use the project-contained zcp@1 workspace with Claude Code, browser VS Code, and project-scoped ZCP access.' --- -Remote setup creates an isolated agent workspace inside the Zerops project. The `zcp@1` service runs the same `zcp` binary used by local setup, but the agent, terminal, and optional browser IDE operate from a clean project service instead of your laptop. +Remote setup gives you an agent workspace inside the Zerops project. -That isolation is the reason remote setup is the recommended first path. Zerops can preconfigure a more permissive agent workspace, project-private networking, and project-scoped credentials without exposing your local machine. +The `zcp@1` service runs the same `zcp` binary used by local setup, but the agent, terminal, and optional browser IDE run in a clean project service instead of on your laptop. That is the main advantage: Zerops can provide a low-friction agent workspace with project-private networking and broader shell permissions while keeping that blast radius inside the project. -In most dashboard paths, there is little local setup to do. A recipe or project toggle creates the workspace service, injects the ZCP token, and can include Claude Code plus browser VS Code. This page is about choosing that workspace path, opening it, and understanding what belongs in the workspace versus what belongs in app runtime services. +Use this path when you want a project-contained workspace, a safer default boundary for broad agent permissions, or a preconfigured environment with Claude Code and browser VS Code. -App code still deploys to runtime services such as `appdev`, `appstage`, or `app`; the `zcp` service is the workspace and control surface, not the application runtime. +App code still deploys to your app runtime services. The `zcp` service is the workspace and control surface; it is not the application runtime. -For the local alternative, see [Set up local ZCP](/zcp/setup/local-agent-bridge). For the tradeoffs, see [Choose remote or local setup](/zcp/setup/choose-workspace). - -## What the remote workspace gives you +## What you get -| Part | What it does | -| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `zcp@1` service | Runs the `zcp` binary inside the project and exposes project-scoped ZCP operations to the agent. | -| `ZCP_API_KEY` | Injected automatically by Zerops, scoped to this one project. Do not set it by hand in remote setup. | -| **Include Coding Agent** | Installs and preconfigures the bundled agent CLI. The current dashboard flow bundles Claude Code; support for more agents is planned. | -| Claude Code auth | Uses your Claude subscription login or API token. Zerops wires Claude Code to ZCP, but the agent account stays yours. | -| **Cloud IDE** | Adds browser-based VS Code for editing, terminals, and supervising the agent session. | -| Workspace toolchain | Provides a shell, `zcli`, common CLI tools, database clients, browser helpers, and the project-local ZCP connection as a starting workspace. | -| Project-private network | Lets the workspace reach project services such as databases and caches by hostname. Your laptop does not need VPN for the remote workspace. | -| Permission boundary | Keeps broad agent command permissions inside the Zerops project service instead of on your laptop. The bundled remote profile is intentionally low-friction; do not copy that posture to local setup. | +- **`zcp@1` workspace service.** Runs ZCP inside the project and gives the agent project-scoped operations. +- **Platform-injected `ZCP_API_KEY`.** Zerops injects the project token into the workspace; you normally do not set it by hand. +- **Bundled Claude Code when enabled.** The **Include Coding Agent** option installs and preconfigures Claude Code. You authenticate with your own Claude subscription login or API key. More bundled agents are planned. +- **Browser VS Code when enabled.** The **Cloud IDE** option gives you a browser editor, terminal, and a place to supervise or take over the agent session. +- **Project-private networking.** The workspace can reach managed services by hostname without laptop VPN. +- **Open workspace model.** You can add other agent CLIs, private MCP servers, helper processes, dotfiles, package installs, or a derived team image. -The model account, GitHub account, package registry login, and other external credentials are still yours to provide to the tools that need them. - -## Get a remote workspace +For the local alternative, see [Set up local ZCP](/zcp/setup/local-agent-bridge). For the tradeoffs, see [Choose remote or local setup](/zcp/setup/choose-workspace). -There are three common dashboard paths. They all produce the same kind of `zcp@1` workspace service. +## Choose a starting path ### Recipe with AI Agent environment -Use this path when you want the shortest first run or a known stack baseline. +Use this when you want a guided path or a known stack baseline. 1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes). -2. Open a recipe that offers an **AI Agent** environment, for example [Laravel showcase](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent). -3. Select **AI Agent**. -4. Keep **Coding Agent** and **Cloud IDE** enabled for the first run. +2. Choose a recipe that offers an **AI Agent** environment, for example [Laravel showcase](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent). +3. Keep the AI Agent environment selected. +4. Keep **Coding Agent** and **Cloud IDE** enabled when you want the bundled agent and browser workspace. 5. Deploy the recipe. -The recipe creates app services and managed services, then adds the `zcp` workspace. After the workspace is ready, normal app work continues through [Build with ZCP](/zcp/workflows/build-with-zcp). +The recipe creates app services, managed services, and the `zcp@1` workspace together. After provisioning, continue with a product prompt in [Build with ZCP](/zcp/workflows/build-with-zcp). ### New project with ZCP enabled -Use this path when you want a blank or custom project. +Use this when you want a blank or custom project. 1. Open [Add new project](https://app.zerops.io/dashboard/project-add). 2. Enter the project name, region, and tags. -3. Toggle **Add Zerops Control Plane (ZCP) service**. -4. In **ZCP CONFIGURATION**, keep **Include Coding Agent** enabled if you want bundled Claude Code. +3. Enable **Add Zerops Control Plane (ZCP) service**. +4. Keep **Include Coding Agent** enabled if you want bundled Claude Code. 5. Keep **Cloud IDE** enabled if you want browser VS Code. -6. Submit the project. +6. Create the project. -The result is a project with a `zcp@1` workspace. The app runtime and managed services may still need to be created later as part of normal ZCP app work. +This gives you a project with the `zcp@1` workspace. App runtimes and managed services may still be created later by normal ZCP app work. ### Existing project -Use this path when the project already has runtime or managed services. - -Add a `zcp` service from the project dashboard the same way you add any other Zerops service. The workspace appears next to the existing services and receives project-scoped ZCP access from the platform. - -Do not treat the existing services as automatically correct for the next app task. The agent should read project state and use or change services only as the requested work requires; that workflow belongs in [Build with ZCP](/zcp/workflows/build-with-zcp). - -## Configure the ZCP service +Use this when the project already has runtime or managed services. -The dashboard configuration controls what the remote workspace includes. +Add a `zcp` service from the project dashboard the same way you add another Zerops service. The workspace appears next to the existing services and receives project-scoped ZCP access from the platform. -| Option | What to decide | -| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Coding Agent | Enable it when you want Claude Code installed and preconfigured for this project. Authenticate with your Claude subscription login or API token. | -| Cloud IDE | Enable it when you want browser VS Code. Choose public access when you need browser access without VPN; choose VPN-only access when the workspace should stay private. | -| Additional changes | Review the platform changes before deploy. Zerops creates and injects the project-scoped token ZCP needs, and enables the workspace access required for project operations. | -| Workspace access | Keep the default remote permission posture when you want low-friction agent work inside the isolated project workspace. Do not mirror that posture onto local setup. | -| Team-specific tooling | Add package installs, dotfiles, private MCP servers, helper processes, or derived images when your team needs them persistently. | - -These choices affect the workspace, not the app runtime. Runtime services own app builds and deploys. +The agent should still read project state before changing anything. Existing services are context, not automatic instructions for the next task. ## Open the workspace -After the `zcp` service reaches running state: +After the `zcp` service is running: 1. Open the project in the [Zerops dashboard](https://app.zerops.io/). 2. Open the `zcp` service. 3. Use **Browser VS Code** when **Cloud IDE** is enabled. 4. Complete the Claude Code login or API-token flow if prompted. -After authentication, Claude Code is installed, connected to this project's ZCP service, and ready to work from the browser VS Code terminal. The first app prompt belongs in [Build with ZCP](/zcp/workflows/build-with-zcp), not in setup. +After authentication, Claude Code is connected to this project's ZCP service and ready to work from the browser VS Code terminal. First load can take a minute while the image, Cloud IDE, and bundled agent finish starting. If the page opens before the service is ready, wait until the service is running and reload. -If the service was exposed on a `.zerops.app` subdomain, public routing follows the normal Zerops [public access](/features/access) model. The workspace still uses the project-scoped token inside the service. +## Use your own editor or tools + +Browser VS Code is the quickest entry point, not the only one. -## Use a desktop remote editor +Editors that support remote development can connect to the workspace or runtime services over SSH, depending on how your team wants to work. Common options include VS Code Remote SSH, JetBrains Gateway, Cursor, Zed, and plain SSH. -Browser VS Code is the quickest way into the remote workspace, but it is not the only editing path. Editors that support remote development can connect over SSH to the `zcp` service or directly to runtime services, depending on how your team wants to work. +This keeps the agent and project-private network inside Zerops while letting you use a desktop editor UI. Broader editor patterns live in [Local & Remote Development](/features/local-remote-development#native-ide-over-ssh). -Common options include VS Code Remote SSH, JetBrains Gateway, Cursor, Zed, and plain SSH. This keeps the agent and project-private network inside Zerops while letting you use a local desktop editor UI. +You can also install another agent CLI or additional MCP servers inside the `zcp` service. The bundled Claude Code flow is a convenience, not a closed product boundary. + +## What belongs where -For the broader development model, see [Local & Remote Development](/features/local-remote-development#native-ide-over-ssh). +| Concern | Where it belongs | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| Agent workspace | The `zcp@1` service: agent CLI, browser VS Code, shell tools, MCP servers, helper processes, dotfiles. | +| App code deploys | Your app runtime services; not the `zcp` workspace service. | +| ZCP project token | Injected by Zerops into the `zcp` workspace. | +| Agent account | Your Claude subscription login or model API credential. Zerops wires the agent to ZCP, but the agent account remains yours. | +| Git credentials | Configured inside the workspace when the agent should commit or push from remote setup. | +| Production promotion | A separate production project and release path. | ## Runtime file access Remote setup can mount runtime service filesystems into the `zcp` workspace after the first setup pass. Each mounted runtime appears as its own folder. Editing a mounted file changes the file inside that runtime service, with no upload step from your laptop. -Filesystem reach is intentionally narrower than network reach. The workspace can reach project services over the private network, but it only sees runtime files that are mounted into it. +Filesystem reach is narrower than network reach. The workspace can reach project services over the private network, but it only sees runtime files that are mounted into it. ## Make customization persistent @@ -120,18 +109,8 @@ Use these patterns: Keep app runtime build steps with the runtime services. The remote workspace carries the agent, tools, and project access; runtime services own app builds and deploys. -## What belongs where - -| Concern | Where it belongs | -| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Agent workspace | The `zcp@1` service, with Claude Code, browser VS Code, shell tools, MCP servers, and team-specific helpers. | -| App code deploys | Runtime services such as `appdev`, `appstage`, or `app`; not the workspace service. | -| ZCP project token | Injected by Zerops into the remote workspace. You normally do not set `ZCP_API_KEY` by hand in remote setup. | -| Agent account | Your Claude subscription login or model API credential. Zerops wires the agent to ZCP, but the agent account remains yours. | -| Production promotion | A separate production project and release path. Remote setup belongs in development or staging projects; see [Production boundary](/zcp/security/production-policy). | - ## Next steps - [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. -- [Trust model](/zcp/security/trust-model) - what the project boundary covers. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - how `ZCP_API_KEY`, git credentials, and CI secrets differ. +- [Production boundary](/zcp/security/production-policy) - why production belongs in a separate project. diff --git a/apps/docs/content/zcp/setup/local-agent-bridge.mdx b/apps/docs/content/zcp/setup/local-agent-bridge.mdx index 3ce7badff..5bac7b4af 100644 --- a/apps/docs/content/zcp/setup/local-agent-bridge.mdx +++ b/apps/docs/content/zcp/setup/local-agent-bridge.mdx @@ -1,55 +1,49 @@ --- title: 'Set up local ZCP' -description: 'Install the zcp binary, run zcp init in your project, and let a local coding agent operate one Zerops project.' +description: 'Install the zcp binary, initialize a local project directory, and connect a local agent to one Zerops project.' --- -Local setup runs the `zcp` binary on your machine and connects it to one project directory. Your local agent works next to files and data on your machine, your editor and terminal stay local, your git credentials stay local, and ZCP gives the agent project-scoped Zerops operations. +Local setup runs ZCP on your machine. -Use local setup when your normal local environment should stay in charge: a desktop editor, local files, local data, familiar terminal tooling, local git credentials, lower-latency feedback, or an agent client that is not covered by remote setup. It can also reduce the need to keep a remote dev runtime running during inner-loop development, although a Zerops runtime is still needed when the agent should deploy and verify on Zerops. +Use it when the agent should work next to local files, local data, your desktop editor, your terminal tools, and your local git credentials. ZCP still scopes Zerops operations to one project, but the agent client itself runs as your local user. Your local agent permissions and filesystem allowlists matter. -The tradeoff is local machine exposure. ZCP is still scoped to one Zerops project, but the agent client runs as your local user and can reach whatever your client permissions allow. If you are trying ZCP for the first time, [remote setup](/zcp/setup/hosted-workspace) is usually safer and faster. +[Remote setup](/zcp/setup/hosted-workspace) is the safer default when you do not need local files or tools. Use local setup when the local workflow is the point. :::warning Public preview -Local setup is the part most likely to change. The binary install path, `.mcp.json` shape, and files written by `zcp init` are still settling between releases. +Local setup has more moving parts than remote setup and may change faster. The binary install path, `.mcp.json` shape, and files written by `zcp init` are still settling between releases. ::: -## What local setup changes +## Choose a local starting point + +Pick the folder that should own app work: + +- **Empty local directory.** Start with no app code yet. The agent can create the app shape and use ZCP to select or create Zerops services. +- **Existing project directory.** Use this when app code, local data, editor setup, test fixtures, and git credentials already live on your machine. +- **Recipe prepared for local setup.** Use a recipe to create the Zerops service baseline, then run the agent locally from the directory that should own source changes. + +After that choice, the mechanics are the same: install `zcp`, run `zcp init`, add a project-scoped token, start VPN when private service access is needed, and link a deploy target when the agent should deploy. + +## What local setup gives the agent -| Area | Local setup behavior | -| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| ZCP process | Runs on your machine as `zcp serve`, launched by your agent client from the project folder. | -| Agent process | Runs as your local user through your editor or CLI agent. ZCP stays project-scoped, but the agent client can reach whatever your local permissions allow. | -| Source files | The agent edits files on your machine. Deploys use your working directory as the source. | -| Local tooling | Your normal terminal, framework CLI, editor runner, and local feedback loop stay in charge. | -| Private network | Your local app and shell reach project services through `zcli vpn up `. | -| Project token | You add a project-scoped `ZCP_API_KEY` to the local `.mcp.json` env block. | -| Agent credentials | Stay with your local agent client. ZCP does not see the agent subscription or model login. | -| Git credentials | Your local git CLI, SSH agent, or credential helper handles pushes. | -| Cost profile | No remote `zcp` workspace service. Deploy targets and managed services still cost when they run. | +- **Local files as source.** The agent edits the directory on your machine, and deploys use that working directory. +- **Your editor and terminal.** Framework CLIs, test runners, local data, and local feedback stay under your normal tools. +- **Your git credentials.** Pushes use your local git CLI, SSH agent, or credential helper. +- **Project-scoped Zerops operations.** ZCP lets the agent discover services, generate env snapshots, deploy to linked runtimes, read logs, and verify the project. +- **Private service access through VPN.** Your local app and shell reach project services through `zcli vpn up`. -Security note: local ZCP cannot protect your laptop from the agent client. Set your client permissions, filesystem allowlists, and shell approval rules as you would for any local coding agent. +Security note: local ZCP cannot protect your laptop from the agent client. Configure your local agent approvals the same way you would for any coding agent running on your machine. ## 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). +- A Zerops project. Create one from the [Zerops dashboard](https://app.zerops.io/dashboard/project-add), from a [recipe](https://app.zerops.io/recipes), or use an existing development/staging project. - [zCLI](/references/cli) installed and authenticated on your machine. - A compatible local agent client installed and logged in. - A **project-scoped Zerops token** for exactly one project. Multi-project tokens are refused at startup. - A local directory where the agent should run. -You do not need ZCP just to develop locally against Zerops services. `zcli vpn up` plus your editor is enough for human local development. Add ZCP when a local coding agent should also discover services, generate env snapshots, deploy, read logs, and verify the project. - -## Choose a local starting point - -Local setup is not limited to an existing app directory. Pick the source that matches the work: - -- **Empty local directory.** Use this when you want the agent to create the app shape from a product request, then use ZCP to provision or select Zerops services. -- **Existing project directory.** Use this when the app code, local data, editor setup, git credentials, and team tooling already live on your machine. -- **Recipe prepared for local setup.** Some recipes provide local-oriented environments in addition to remote AI Agent environments. Use that path when you want a known Zerops service baseline from a recipe, but want the agent, editor, files, and local tooling to stay on your machine. - -After that choice, the local mechanics are the same: initialize ZCP in the chosen directory, add the project-scoped token, use VPN when private service access is needed, and link a runtime when the agent should deploy. +You do not need ZCP just to develop locally against Zerops services as a human. `zcli vpn up` plus your editor is enough for that. Add ZCP when a local coding agent should also understand and operate the Zerops project. -## 1. Get a project-scoped Zerops token +## 1. Get a project-scoped token ZCP needs a Zerops API token scoped to exactly one project. For normal agent work, use a single-project token with full access to that project. Read-only tokens can authenticate but fail on deploys, env changes, lifecycle actions, and other mutations. @@ -69,7 +63,7 @@ zcp version If your shell cannot find `zcp`, add the install directory to `PATH` and reload the shell. -## 3. Run `zcp init` in the project folder +## 3. Run `zcp init` From the local directory the agent should operate: @@ -89,7 +83,7 @@ Current generated artifacts: Re-running `zcp init` may refresh generated config. `CLAUDE.md` preserves edits outside managed markers, but `.mcp.json` is regenerated from the token-less template. If you rerun it, re-check the `ZCP_API_KEY` env block before launching the agent again. -## 4. Add `ZCP_API_KEY` to `.mcp.json` +## 4. Add `ZCP_API_KEY` `zcp init` writes a token-less `.mcp.json`. Add the project token under the `env` block: @@ -121,9 +115,9 @@ Sanity check: Use ZCP to list the services in this project. ``` -A working connection answers with the project's runtime and managed services. If the agent cannot reach ZCP, check the launch directory, token scope, and whether the client actually loaded `.mcp.json`. +A working connection answers with the project's runtime and managed services. If the agent cannot reach ZCP, check the launch directory, token scope, and whether the client loaded `.mcp.json`. -## 6. Bring up the Zerops VPN +## 6. Bring up VPN when private services are needed ZCP can talk to the Zerops API without VPN. Your local app, shell, tests, database clients, and framework commands need VPN to reach project-private service hostnames such as `db` or `cache`. @@ -135,7 +129,7 @@ VPN setup needs admin or root approval on macOS and Linux. ZCP can tell the agen After the tunnel is up, project service hostnames resolve from your machine. Laptop sleep, network changes, and idle time can drop the tunnel; run the command again when local service connections start failing. -## 7. Generate a local env snapshot when needed +## 7. Generate local env when needed When the local app needs project credentials, ask the agent for the app work and let ZCP generate the env bridge as part of that work. For a standalone check: diff --git a/apps/docs/sidebars.js b/apps/docs/sidebars.js index 057166176..9bd903d76 100644 --- a/apps/docs/sidebars.js +++ b/apps/docs/sidebars.js @@ -626,12 +626,12 @@ module.exports = { { type: 'doc', id: 'zcp/setup/hosted-workspace', - label: 'Use remote ZCP workspace', + label: 'Remote workspace', }, { type: 'doc', id: 'zcp/setup/local-agent-bridge', - label: 'Set up local ZCP', + label: 'Local setup', }, ], }, diff --git a/apps/docs/src/css/custom.css b/apps/docs/src/css/custom.css index 47de71cdf..1c210ea20 100644 --- a/apps/docs/src/css/custom.css +++ b/apps/docs/src/css/custom.css @@ -424,6 +424,53 @@ html[data-theme='dark'] .docsearch-btn:hover { color: #5b45b0; } +.zcp-choice-card { + background: #ffffff; + border: 1px solid #d7dce5; + border-radius: 8px; + color: #172033; + display: flex; + flex-direction: column; + gap: 0.75rem; + padding: 1rem; +} + +.zcp-choice-card h3 { + font-size: 1rem; + line-height: 1.25; + margin: 0; +} + +.zcp-choice-card p { + color: #4b5563; + font-size: 0.88rem; + line-height: 1.5; + margin: 0; +} + +.zcp-choice-card ul { + display: grid; + gap: 0.42rem; + margin: 0; + padding-left: 1rem; +} + +.zcp-choice-card li { + color: #4b5563; + font-size: 0.84rem; + line-height: 1.4; +} + +.zcp-choice-card a { + align-self: flex-start; + font-size: 0.86rem; + font-weight: 650; +} + +.zcp-choice-card--compact { + gap: 0.6rem; +} + html[data-theme='dark'] .zcp-lifecycle-prompt { background: #132033; color: #f8fafc; @@ -454,6 +501,16 @@ html[data-theme='dark'] .zcp-lifecycle-stage--delivery { background: rgba(76, 29, 149, 0.22); } +html[data-theme='dark'] .zcp-choice-card { + background: rgba(24, 24, 27, 0.72); + color: #f8fafc; +} + +html[data-theme='dark'] .zcp-choice-card p, +html[data-theme='dark'] .zcp-choice-card li { + color: #cbd5e1; +} + @media (min-width: 768px) { .zcp-lifecycle-prompt { margin-left: 0; From c29eaffaa0338cf2bda087ee1dfb427e0b9ff66f Mon Sep 17 00:00:00 2001 From: krls2020 Date: Sun, 10 May 2026 18:02:48 +0200 Subject: [PATCH 09/24] Refine ZCP quickstart recipe flow --- apps/docs/content/zcp/quickstart.mdx | 46 +++++++++++-------- .../content/zcp/setup/hosted-workspace.mdx | 10 ++-- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/apps/docs/content/zcp/quickstart.mdx b/apps/docs/content/zcp/quickstart.mdx index 4b95544a6..6cb458a07 100644 --- a/apps/docs/content/zcp/quickstart.mdx +++ b/apps/docs/content/zcp/quickstart.mdx @@ -1,22 +1,22 @@ --- -title: "Quickstart" -description: "The fastest practical way to try ZCP: deploy an AI Agent recipe, authenticate Claude Code, open Browser VS Code, and ask for an app change." +title: 'Quickstart' +description: 'Try ZCP through a ready-made AI Agent recipe: deploy Laravel showcase, authorize Claude Code, open Browser VS Code, and ask for a product change.' --- import Image from '/src/components/Image'; -This is the fastest way to try ZCP and understand it in practice. You deploy a ready-made recipe, authenticate the bundled agent, open the browser workspace, and ask for an app change in natural language. +Use this path when you want to see ZCP working with the least setup. It is intentionally one path: deploy a recipe that already includes the **AI Agent** environment, authorize Claude Code, open Browser VS Code, and ask for product work in natural language. -Zerops has a [recipes catalog](https://app.zerops.io/recipes) covering many runtimes and frameworks. Recipes can ship multiple environments for the same stack; the **AI Agent** environment is the ready-to-go ZCP setup. It creates app services plus the `zcp@1` workspace service, browser VS Code, and bundled agent wiring in one deploy. +Zerops has a [recipes catalog](https://app.zerops.io/recipes) for many runtimes and frameworks. Some recipes include an **AI Agent** environment, which is the ready-to-go ZCP setup: app services, managed services, the `zcp@1` workspace service, Browser VS Code, and bundled agent wiring in one deploy. -This quickstart uses the [Laravel showcase recipe](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent) because it includes real managed services and an AI Agent environment. The same path works for other recipes that offer AI Agent. +This quickstart uses the [Laravel showcase recipe](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent) because it includes real managed services and the AI Agent environment. The same flow works for other recipes that offer AI Agent. ## Prerequisites - A Zerops account with permission to create a project. -- A Claude Code subscription login or API authentication. Claude Code is the bundled agent in the current dashboard flow; support for additional agents is planned. Zerops wires the agent to ZCP, but your agent subscription or model credentials stay yours. +- A Claude Code subscription login or API authentication. Claude Code is the bundled agent in the current dashboard flow; support for additional bundled agents is planned. Zerops wires Claude Code to ZCP, but your agent subscription or model credentials stay yours. -## 1. Deploy the AI Agent recipe +## 1. Choose the AI Agent recipe 1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes). 2. Open a recipe with an **AI Agent** environment. For example, open [Laravel showcase](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent). @@ -34,11 +34,13 @@ This quickstart uses the [Laravel showcase recipe](https://app.zerops.io/recipes /> -The deploy creates a development runtime, a staging runtime, managed dependencies, and a `zcp` service. The `zcp` service is the workspace where the agent operates from; app code still deploys to the app runtimes, not to `zcp`. +The deploy creates the app runtimes, managed dependencies, and the `zcp@1` workspace service together. The `zcp` service is where the agent, terminal, and browser IDE run. App code still deploys to the app runtime services, not to `zcp`. -## 2. Authenticate Claude Code +## 2. Authorize Claude Code -When provisioning finishes, the dashboard opens the agent authentication flow. Use your own Claude Code subscription login or API credentials. Zerops wires the agent to this project through ZCP, but the agent account stays yours. +When provisioning finishes, the dashboard opens the Claude Code authentication flow. Use your own Claude Code subscription login or API credentials. + +This is separate from the ZCP project token. Zerops injects the project-scoped `ZCP_API_KEY` into the workspace service; your Claude account or API key is used only by the bundled agent. If you close the prompt, open the project in the dashboard and then open the `zcp` service. The control-plane panel shows the browser workspace, web terminal, SSH path, desktop IDE path, and agent authorization state. @@ -52,9 +54,9 @@ If you close the prompt, open the project in the dashboard and then open the `zc /> -## 3. Open VS Code and Claude Code +## 3. Open the workspace -After authentication, continue into **Browser VS Code**. The workspace opens with the project filesystem, ZCP configuration, and the Claude Code panel available in the editor. +After authentication, continue into **Browser VS Code**. The workspace opens with the project filesystem, ZCP configuration, terminal access, and the Claude Code panel available in the editor.
+You are now inside the remote ZCP workspace. The agent can read project state, use project-scoped ZCP operations, reach project-private services, and deploy app changes to the runtime services created by the recipe. + ## 4. Ask for a product outcome -In Claude Code, ask for the app behavior in natural language: +In Claude Code, ask for the app behavior in natural language. A good first prompt is intentionally short: ```text Build a task board for my team. ``` -Do not add "deploy it", "verify it", or "send me the URL" just to make the agent finish the work. With ZCP, those are part of the expected app task. Add constraints only when they change the product, stack, runtime target, acceptance criteria, or delivery path: +Do not add "deploy it", "verify it", or "send me the URL" just to make the agent finish the work. With ZCP, those are part of the expected app task. + +Add details when they change the product, stack, runtime layout, acceptance criteria, or delivery path: ```text Build a Node.js task board with latest PostgreSQL on dev+stage. ``` -## 5. Inspect the result +## 5. Read the proof -The final answer should give you a real URL, endpoint, or UI proof. Open the URL and try the core task-board behavior the agent says it verified. +The final answer should give you a real URL, endpoint, UI state, stored result, or a concrete blocker. Open the URL and try the core behavior the agent says it verified. If you used the technical prompt above, create a task, refresh the page, and confirm the task is still there. If it disappears, the app is not done; ask the agent to verify that exact behavior again and continue from the project evidence rather than starting over. @@ -90,8 +96,10 @@ If the agent cannot finish, the useful output is a concrete blocker: the missing ## Next steps -Read [How ZCP works](/zcp/concept/how-it-works) next if you want the model behind what just happened: project state, runtime fit, app wiring, deploy, verification, proof, and blockers. +Read [How ZCP works](/zcp/concept/how-it-works) if you want the model behind what just happened: project state, runtime fit, app wiring, deploy evidence, behavior proof, and blockers. + +Read [Choose remote or local setup](/zcp/setup/choose-workspace) when you are deciding where the agent workspace should live for a real project. Remote setup keeps the agent inside Zerops; local setup keeps the agent next to your local files, editor, data, and tools. -Read [Choose remote or local setup](/zcp/setup/choose-workspace) when you are deciding how to start a real project: AI Agent recipe, new-project toggle, existing project, or local setup beside your own editor. +Read [Build with ZCP](/zcp/workflows/build-with-zcp) before giving the agent larger tasks. It explains the practical choices you can control during normal work: runtime layout, development context, and delivery preference. -Read [Build with ZCP](/zcp/workflows/build-with-zcp) before giving the agent larger tasks. It explains the three practical phases you will use during normal work: infrastructure setup, development and verification, then delivery after proof. +Before promoting anything to production, read [Production boundary](/zcp/security/production-policy). ZCP belongs in development or staging projects; production should stay in a separate Zerops project and receive verified work through your release path. diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index 68530c175..c340dadd0 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -26,15 +26,11 @@ For the local alternative, see [Set up local ZCP](/zcp/setup/local-agent-bridge) ### Recipe with AI Agent environment -Use this when you want a guided path or a known stack baseline. +Use this when you want a guided stack baseline. A recipe with an **AI Agent** environment creates app services, managed services, and the `zcp@1` workspace together. -1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes). -2. Choose a recipe that offers an **AI Agent** environment, for example [Laravel showcase](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent). -3. Keep the AI Agent environment selected. -4. Keep **Coding Agent** and **Cloud IDE** enabled when you want the bundled agent and browser workspace. -5. Deploy the recipe. +Keep **Coding Agent** enabled when you want bundled Claude Code. Keep **Cloud IDE** enabled when you want Browser VS Code in the project. The dashboard click-through is covered in the [Quickstart](/zcp/quickstart). -The recipe creates app services, managed services, and the `zcp@1` workspace together. After provisioning, continue with a product prompt in [Build with ZCP](/zcp/workflows/build-with-zcp). +After provisioning, continue with a product prompt in [Build with ZCP](/zcp/workflows/build-with-zcp). ### New project with ZCP enabled From 87f7a4e16af2ee751bcef65ddb4920935529ba72 Mon Sep 17 00:00:00 2001 From: krls2020 Date: Sun, 10 May 2026 18:11:48 +0200 Subject: [PATCH 10/24] Refine ZCP setup and quickstart framing --- apps/docs/content/features/coding-agents.mdx | 4 ++-- .../features/local-remote-development.mdx | 22 +++++++++---------- .../docs/content/zcp/concept/how-it-works.mdx | 2 +- apps/docs/content/zcp/overview.mdx | 12 +++++----- apps/docs/content/zcp/quickstart.mdx | 8 ++++++- .../zcp/security/production-policy.mdx | 2 +- .../docs/content/zcp/security/trust-model.mdx | 2 +- .../content/zcp/setup/choose-workspace.mdx | 8 +++---- .../content/zcp/setup/hosted-workspace.mdx | 2 +- .../content/zcp/setup/local-agent-bridge.mdx | 2 +- apps/docs/sidebars.js | 2 +- 11 files changed, 36 insertions(+), 30 deletions(-) diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx index 6ff5ba8b0..8367c6e97 100644 --- a/apps/docs/content/features/coding-agents.mdx +++ b/apps/docs/content/features/coding-agents.mdx @@ -51,7 +51,7 @@ The `zcp` binary can run in a Zerops service or on your laptop. The user choice Either way, the agent gets the same project-scoped ZCP operations against the same project. Remote setup can package ZCP with an agent CLI and browser editor; local setup connects your own agent client to ZCP. -Decision: [Choose remote or local setup](/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). +Decision: [Remote or local ZCP setup](/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 remote setup adds @@ -131,7 +131,7 @@ The claim is not that ZCP replaces those tools. It solves the missing operationa Date: Sun, 10 May 2026 18:23:29 +0200 Subject: [PATCH 11/24] Refine ZCP workspace setup choice --- .../content/zcp/setup/choose-workspace.mdx | 29 ++----------------- apps/docs/src/css/custom.css | 4 --- 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index 655c174eb..ff676bca6 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -18,8 +18,8 @@ Both paths use the same `zcp` binary and the same ZCP model: one Zerops project,

Remote setup

The agent runs inside a zcp@1 service in the Zerops project. - This is safer by default because broad agent permissions stay inside a - clean project service, not on your laptop. + Use it when you want the agent workspace, project-private networking, and + broad shell permissions contained inside the project.

  • No local install.
  • @@ -46,31 +46,6 @@ Both paths use the same `zcp` binary and the same ZCP model: one Zerops project,
-## Decide by what should stay close to the agent - -
-
-

Use remote when you want...

-
    -
  • A project-contained workspace.
  • -
  • Clean project-contained isolation.
  • -
  • Bundled Claude Code and Cloud IDE.
  • -
  • Private service access without laptop VPN.
  • -
  • A remote editor path over SSH when browser VS Code is not enough.
  • -
-
-
-

Use local when you want...

-
    -
  • Your local editor and terminal to stay in charge.
  • -
  • Local files, fixtures, or data next to the agent.
  • -
  • Your existing local git credentials and helper tools.
  • -
  • An agent client or workflow the remote workspace does not cover.
  • -
  • Local agent permissions you explicitly control.
  • -
-
-
- Remote setup usually costs one extra workspace service. Local setup avoids that workspace service, but deploy targets and managed services still cost while running. For most teams, the decision should be about where the agent should safely and ergonomically work, not about the small workspace cost. :::info Public preview diff --git a/apps/docs/src/css/custom.css b/apps/docs/src/css/custom.css index 1c210ea20..c8b7909dc 100644 --- a/apps/docs/src/css/custom.css +++ b/apps/docs/src/css/custom.css @@ -467,10 +467,6 @@ html[data-theme='dark'] .docsearch-btn:hover { font-weight: 650; } -.zcp-choice-card--compact { - gap: 0.6rem; -} - html[data-theme='dark'] .zcp-lifecycle-prompt { background: #132033; color: #f8fafc; From ce37e3d83a9b3930848ce4cec4b1e8c730d3b13f Mon Sep 17 00:00:00 2001 From: krls2020 Date: Sun, 10 May 2026 20:21:26 +0200 Subject: [PATCH 12/24] Refine ZCP docs and quickstart imagery --- apps/docs/content/features/coding-agents.mdx | 46 +- .../features/local-remote-development.mdx | 50 +- .../docs/content/zcp/concept/how-it-works.mdx | 10 +- apps/docs/content/zcp/glossary.mdx | 2 +- apps/docs/content/zcp/overview.mdx | 8 +- apps/docs/content/zcp/quickstart.mdx | 25 +- .../security/tokens-and-project-access.mdx | 14 +- .../content/zcp/setup/choose-workspace.mdx | 14 +- .../content/zcp/setup/hosted-workspace.mdx | 26 +- .../content/zcp/setup/local-agent-bridge.mdx | 2 +- .../zcp/workflows/build-and-verify-app.mdx | 7 +- .../content/zcp/workflows/build-with-zcp.mdx | 57 +- .../workflows/create-or-adopt-services.mdx | 7 +- .../zcp/workflows/delivery-handoff.mdx | 7 +- apps/docs/sidebars.js | 4 +- apps/docs/src/css/custom.css | 31 + .../img/zcp/quickstart-zcp-service-ready.png | Bin 37871 -> 268213 bytes apps/docs/static/llms-full.txt | 1941 ++++++++++------- apps/docs/static/llms-small.txt | 1941 ++++++++++------- apps/docs/static/llms.txt | 3 - zerops-llm-script.ts | 23 +- 21 files changed, 2529 insertions(+), 1689 deletions(-) diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx index 8367c6e97..1e482d9cc 100644 --- a/apps/docs/content/features/coding-agents.mdx +++ b/apps/docs/content/features/coding-agents.mdx @@ -3,8 +3,6 @@ title: ZCP for Coding Agents description: ZCP gives coding agents current project knowledge and bounded operations on one Zerops project — discover, deploy, verify, recover, and hand off. --- -import CustomCard from '/src/components/CustomCard'; - Coding agents need current project knowledge and a safe way to act on it. The hard part is rarely "write the function" — it's letting the agent connect product intent to infrastructure: services, env vars, logs, deploys, verification, and a path to production. **Zerops Control Plane (ZCP)** gives a coding agent current project state and bounded operations on one Zerops project. You describe the app or change you want; ZCP lets the agent discover services, use existing infrastructure or create what is missing, wire the app to managed services, deploy through the standard pipeline, verify the real endpoint, inspect logs, and report a URL or blocker. You hold the product intent, constraints, approvals, and quality bar. @@ -40,14 +38,9 @@ Full workflow terms: [Workflow terms](/zcp/reference/agent-workflow). The `zcp` binary can run in a Zerops service or on your laptop. The user choice is remote setup or local setup. -
- - A Zerops service (`zcp@1`) inside the project runs ZCP. Enable **Include Coding Agent** to add the bundled agent CLI and preconfigure it to use ZCP. Enable Cloud IDE when you want browser VS Code. **Recommended starting point.** [Set up →](/zcp/setup/hosted-workspace) - - - The `zcp` binary on your laptop, initialized in a project folder with `zcp init`. The agent runs in your local editor or CLI; private service access uses `zcli vpn up`. [Set up →](/zcp/setup/local-agent-bridge) - -
+**Remote setup** runs ZCP inside a Zerops `zcp@1` service. Enable **Include Coding Agent** for the bundled agent CLI, and **Cloud IDE** for Browser VS Code. This is the recommended starting point when broad agent permissions can stay inside the project. [Use remote workspace](/zcp/setup/hosted-workspace) + +**Local setup** runs the `zcp` binary on your laptop after `zcp init`. The agent runs in your local editor or CLI; private service access uses `zcli vpn up`. Use it when local files, tools, data, or a local-only agent client should stay in charge. [Set up local ZCP](/zcp/setup/local-agent-bridge) Either way, the agent gets the same project-scoped ZCP operations against the same project. Remote setup can package ZCP with an agent CLI and browser editor; local setup connects your own agent client to ZCP. @@ -55,15 +48,9 @@ Decision: [Remote or local ZCP setup](/zcp/setup/choose-workspace). First time? ## What remote setup adds -Remote setup starts with a `zcp@1` Zerops service. Two dashboard options decide what is bundled into that service: - -**Include Coding Agent.** Adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use ZCP running inside the `zcp@1` service. Agent authentication is handled separately by that agent's login method, subscription, or API key. - -**Cloud IDE.** Browser-based VS Code (code-server) with 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 → Cloud IDE](/features/local-remote-development#cloud-ide-on-remote-setup). - -Without **Include Coding Agent**, the service can still exist, but there is no preconfigured agent waiting inside it. Without **Cloud IDE**, there is no browser editor. With both enabled, you get a one-click agent + IDE setup inside the project. +Remote setup starts with a `zcp@1` Zerops service. **Include Coding Agent** adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use ZCP. **Cloud IDE** adds Browser VS Code so you can supervise or take over from the dashboard. -Both options are configured when you provision the service: [Use remote ZCP workspace](/zcp/setup/hosted-workspace#what-you-get). +Without **Include Coding Agent**, there is no preconfigured agent waiting inside the service. Without **Cloud IDE**, there is no browser editor. Configure both when you provision the service if you want the first-run agent + IDE path. Details: [Use remote workspace](/zcp/setup/hosted-workspace#what-you-get). Remote setup is still your workspace. You can install another agent CLI, add private MCP servers or helper processes, edit `CLAUDE.md` or other agent instruction files, and keep team tooling in the service. ZCP supplies the project-scoped control plane, operations, and workflow evidence; it does not lock the service to one agent or one set of tools. @@ -72,7 +59,7 @@ Remote setup is still your workspace. You can install another agent CLI, add pri 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. +- Resource allocation and scaling limits are queryable. When a change affects cost, the agent can surface the proposed resource change and ask 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. @@ -81,19 +68,12 @@ ZCP is the ergonomic layer on top of Zerops APIs, `zcli`, and SSH for agents. It ## Operations and permissions -ZCP groups its operations into three categories so an agent client or team policy can gate them: +ZCP groups its operations so an agent client or team policy can gate them: -
- - Inspect state, logs, events, configuration, and verification output. Safe to auto-allow. [Full list →](/zcp/reference/mcp-operations#read-only-operations) - - - Deploy, change env vars, manage lifecycle, scale, or delete services. Require team policy and confirmation where needed. [Full list →](/zcp/reference/mcp-operations#mutating-operations) - - - Set up infrastructure, mounts, imports, work sessions, or delivery configuration. Gated by team policy. [Full list →](/zcp/reference/mcp-operations#operational-setup) - -
+- **Read-only operations** inspect state, logs, events, configuration, and verification output. [Full list](/zcp/reference/mcp-operations#read-only-operations) +- **Mutating operations** deploy, change env vars, manage lifecycle, scale, or change public access. They are governed by token permissions and your agent/team policy. [Full list](/zcp/reference/mcp-operations#mutating-operations) +- **Destructive operations** have extra ZCP gates: service deletion needs explicit same-conversation approval naming the service, and destructive replacement after failed deploy history requires evidence review first. [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) +- **Operational setup** covers infrastructure, mounts, imports, work sessions, and delivery configuration. [Full list](/zcp/reference/mcp-operations#operational-setup) That's the surface. The boundary is the project. The token authorizes one project, full stop — cross-project, account-wide, or organization actions stay outside reach. Integration tokens are still bounded by the permissions of the user who created them and by the project access selected for the token; see [Roles & Permissions](/features/rbac). Full blast-radius framing: [Trust model](/zcp/security/trust-model). @@ -101,13 +81,13 @@ Full operation list and permission semantics: [Advanced operations](/zcp/referen ## Customize remote setup -Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product - install packages, edit agent instructions, configure additional MCP servers, run helper processes alongside the agent, or ship a hardened version with team-standard tools baked in. Patterns: [Make customization persistent](/zcp/setup/hosted-workspace#make-customization-persistent). +Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product. Install trusted tools, edit agent instructions, configure additional MCP servers, or ship a hardened version with team-standard tools baked in. Patterns and guardrails: [Make customization persistent](/zcp/setup/hosted-workspace#make-customization-persistent). Local setup isn't extensible the same way; the binary on your laptop is just ZCP - editor, shell, local files, local data, and tool feedback stay on your machine. ## Source control -When the agent runs in remote setup, 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. In local setup, it uses your existing git credentials. Detail: [Tokens and credentials -> Git and CI credentials](/zcp/security/tokens-and-project-access#git-and-ci-credentials). +When the agent runs in remote setup, git access belongs inside the workspace: GitHub CLI login, a fine-grained git token stored on the `zcp` service, or SSH agent forwarding from a remote editor. In local setup, it uses your existing local git credentials. Detail: [Tokens and credentials -> Git and CI credentials](/zcp/security/tokens-and-project-access#git-and-ci-credentials). ## What ZCP is not diff --git a/apps/docs/content/features/local-remote-development.mdx b/apps/docs/content/features/local-remote-development.mdx index 7895ecc7b..71627f3c4 100644 --- a/apps/docs/content/features/local-remote-development.mdx +++ b/apps/docs/content/features/local-remote-development.mdx @@ -8,20 +8,20 @@ You can develop on Zerops without leaving your laptop, fully inside Zerops, or a This is also what narrows the gap between development and production. Development and staging can use the same Zerops-managed service types, networking model, and `zerops.yaml` patterns production uses. Production should still live in a separate project with separate credentials, access rules, and resource policies. :::note -Remote setup is a `zcp@1` service you can add to any project. It can provide a Linux dev container with a curated toolchain, optional Cloud IDE, and optional **Include Coding Agent** wiring. The modes below can use that service, connect directly to runtime services, or stay fully local over VPN; local development with `zcli vpn up` does not require ZCP at all. +Remote setup is a `zcp@1` service you can add to a development or staging project. It can provide a Linux dev container, optional Cloud IDE, and optional **Include Coding Agent** wiring. The paths below can use that service, connect directly to runtime services, or stay fully local over VPN; local development with `zcli vpn up` does not 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. +The development question is just: how do you, the developer, get onto that network? Three answers give you the same project-private network reach, with different filesystem, tooling, and credential boundaries. - **Local + VPN.** Your laptop joins the private network via WireGuard. Editor, toolchain, and processes stay where they are. Remote setup isn't required. - **Cloud IDE.** 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 the `zcp` service, or directly to a service container. -Same `db:5432`, same `api:3000`, same project credentials in all three. Switch modes without changing anything about the development project. +Same `db:5432`, same `api:3000`, same project credentials in all three. Switch paths without changing anything about the development project. ## Local + VPN @@ -39,7 +39,7 @@ What you get on your laptop: every service in the project addressable by hostnam What you don't have to do: add remote setup, 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 service type as stage or production. -This mode is also where coding agents running on your laptop fit. Without ZCP, a local agent client inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. To let the agent operate the project - discover, deploy, verify, read logs, and manage delivery handoff - set up [local ZCP](/zcp/setup/local-agent-bridge). +This path is also where coding agents running on your laptop fit. Without ZCP, a local agent client inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. To let the agent operate the project - discover, deploy, verify, read logs, and manage delivery handoff - set up [local ZCP](/zcp/setup/local-agent-bridge). **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. @@ -51,24 +51,16 @@ 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 remote setup (a `zcp@1` service) to a project — see [Set up remote ZCP](/zcp/setup/hosted-workspace) — you get an Ubuntu-based dev container with `zcp`, `zcli`, GitHub CLI, database CLIs, shell utilities, browser automation tools, and SSHFS. Enable **Cloud IDE** for browser-based VS Code; enable **Include Coding Agent** for the bundled agent CLI plus ZCP wiring. +When you add remote setup (a `zcp@1` service) to a development or staging project - see [Use remote workspace](/zcp/setup/hosted-workspace) - you get a project-contained workspace with `zcp`, private-network access, optional Browser VS Code, and optional bundled agent wiring. -Remote setup can also mount 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 Zerops deploy pipeline. One remote setup service can mount multiple dev services at once, covering the whole stack. +Remote setup can also mount runtime service files into the workspace, so you can inspect or edit code that runs in another container without moving it to your laptop. Details live in [Runtime file access](/zcp/setup/hosted-workspace#runtime-file-access). 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.** Remote ZCP isn't on your laptop, so your local SSH keys don't follow it. Two patterns work: +**Source control.** The remote 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 the `zcp` service's secret environment variables; `git` reads it through a credential helper. @@ -87,7 +79,7 @@ ssh frontenddev ssh zcp # if you provisioned the `zcp` service ``` -In this mode, remote setup 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. Remote setup becomes a _convenience_ layer: a single container that carries the toolchain, mounts multiple dev services over SSHFS, and can include `zcli`, Cloud IDE, and coding-agent wiring for the project. If you'd rather connect your editor directly to your runtime services and skip remote setup entirely, that works. +In this path, remote setup 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. Remote setup becomes a convenience layer: a single workspace that can carry tools, runtime file mounts, Cloud IDE, and coding-agent wiring for the project. If you'd rather connect your editor directly to your runtime services and skip remote setup 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 the `zcp` service) uses your laptop's GitHub key over the forwarded agent socket — no token storage required. @@ -102,24 +94,24 @@ Host apidev.zerops See the [SSH reference guide](/references/networking/ssh). ::: -## Picking a mode +## Picking a path -| | Local + VPN | Cloud IDE | Native IDE over SSH | -| ------------------- | ------------------ | -------------------------- | ------------------------------- | -| Editor runs | Local | Browser | Local | -| Toolchain | Local | Remote ZCP | Remote ZCP or service container | -| Dev databases | Hostname via VPN | Native, on private network | Native, on private network | -| Remote ZCP 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 | +| | Local + VPN | Cloud IDE | Native IDE over SSH | +| --------------------- | ------------------ | -------------------------- | --------------------------------- | +| Editor runs | Local | Browser | Local | +| Toolchain | Local | Remote setup | Remote setup or service container | +| Dev databases | Hostname via VPN | Native, on private network | Native, on private network | +| Remote setup 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 remote setup are hitting the same `db:5432`. +You're not locked into one path. The same project can support all three without reprovisioning when your source-of-truth handoff is clear. A teammate on Local + VPN and an agent inside remote setup are hitting the same `db:5432`. For the agent-driven path, the setup decision (remote `zcp@1` service vs. local `zcp` binary) is on its own page: [Remote or local ZCP setup](/zcp/setup/choose-workspace). ## Same architecture, separate production -The development environments above don't approximate production by inventing a different platform. A ZCP development project can run dev and stage services with the same Zerops-managed service types, private networking, load-balancer model, and `zerops.yaml` patterns that production uses in its separate project. Dev, stage, and production differ in resource allocation, access rules, and credentials, not in architecture. +The development environments above don't approximate production by inventing a different platform. A Zerops development/staging project can run dev and stage services with the same Zerops-managed service types, private networking, load-balancer model, and `zerops.yaml` patterns that production uses in its separate project. Dev, stage, and production differ in resource allocation, access rules, and credentials, 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 and stage, you've already tested it against the kind of infrastructure it'll meet in production. The remaining differences are scale, credentials, access policy, and data. @@ -133,7 +125,7 @@ If you've used GitHub Codespaces or Gitpod, Cloud IDE looks similar — a Linux **Dev, staging, and production use the same platform model.** 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 model: managed Postgres, private networking, and `zerops.yaml`. Production still lives in its own project with its own credentials and policies. -Remote ZCP 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. +Remote setup 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 @@ -141,6 +133,6 @@ Remote ZCP is just a Zerops service. It's a container running the `zcp@1` Ubuntu - 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 → [ZCP for Coding Agents](/features/coding-agents) -- Set up remote setup → [Set up remote ZCP](/zcp/setup/hosted-workspace) +- Use remote workspace → [Use remote workspace](/zcp/setup/hosted-workspace) - Remote or local ZCP setup → [Remote or local ZCP setup](/zcp/setup/choose-workspace) - Term reference → [ZCP Glossary](/zcp/glossary) diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx index 5d637a392..4e9a690ad 100644 --- a/apps/docs/content/zcp/concept/how-it-works.mdx +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -80,7 +80,7 @@ ZCP is intentionally opinionated about the concerns an agent has to resolve duri | Project state | Live services, env-var keys, Zerops references, recent events, logs, verification output, and saved work state. | You do not have to paste a service inventory or reconstruct what happened. | | Runtime target | A model for choosing the app runtime that should receive code changes. | The agent can identify where app work belongs before editing or deploying. | | Managed services | Knowledge and env wiring patterns for database, cache, queue, search, storage, mail, and similar services. | Product intent can imply real dependencies without a manual wiring checklist. | -| Service setup | Project-scoped operations for using existing services or creating missing runtimes and dependencies. | Infrastructure shape can be established before app work starts. | +| Service setup | Project-scoped operations for using existing services or creating missing runtimes and dependencies. | Runtime layout and dependencies can be established before app work starts. | | App wiring | Zerops-specific rules for `zerops.yaml`, env references, ports, commands, public access, and build/deploy behavior. | The app is wired for Zerops rather than for a generic cloud template. | | Deploy evidence | Build logs, runtime logs, platform events, service status, HTTP checks, and structured verification output. | A failed deploy becomes a diagnosis surface instead of guesswork. | | Behavior proof | A done gate based on the requested behavior, not only on a successful build or reachable root URL. | The final answer can point to what was actually checked. | @@ -88,17 +88,17 @@ ZCP is intentionally opinionated about the concerns an agent has to resolve duri The exact labels for layouts, delivery modes, and setup routes live in the [Glossary](/zcp/glossary) and [Workflow terms](/zcp/reference/agent-workflow). -## Service setup prepares the project shape +## Service setup prepares the project layout Before app code work starts, ZCP helps the agent make three decisions visible: - Which runtime service is the app target? - Which managed services are dependencies? -- Does the existing project shape fit the request? +- Does the existing project layout fit the request? If the services already exist, the agent can use them. If a needed runtime or dependency is missing, ZCP can create it through project-scoped operations. If the choice affects cost, product scope, credentials, a destructive action, or which stage/runtime should be used, the workflow tells the agent to stop and ask. -Service setup is finished when the app runtime and dependencies are known. It is not the finished app; it is the project shape that lets app work happen in the right place. +Service setup is finished when the app runtime and dependencies are known. It is not the finished app; it is the project layout that lets app work happen in the right place. ## App work changes code and platform wiring together @@ -119,7 +119,7 @@ The first functional deploy goes through ZCP's direct deploy path. That deploy c When something fails, ZCP gives the agent the evidence surface that matches the failure: -| Failure shape | Useful evidence | +| Failure pattern | Useful evidence | | ------------------------ | ------------------------------------------------------------------------------------------------------------ | | Build failed | Build logs, build commands, dependency manifests, deploy file list. | | Runtime failed to start | Runtime or prepare logs, start command, ports, env references. | diff --git a/apps/docs/content/zcp/glossary.mdx b/apps/docs/content/zcp/glossary.mdx index 579a4c702..19f8f317e 100644 --- a/apps/docs/content/zcp/glossary.mdx +++ b/apps/docs/content/zcp/glossary.mdx @@ -9,7 +9,7 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, **ZCP** - Zerops Control Plane for coding agents: the project-scoped control plane documented in this section. -**ZCP operations** - the project-scoped operations surface exposed to a coding agent. In MCP clients, this appears as the ZCP MCP server. +**ZCP operations** - the project-scoped operations surface exposed to a coding agent. In MCP clients, this usually appears as the `zerops` server. **MCP server** - the Model Context Protocol server that exposes ZCP operations to an agent client. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index 9d0c720c4..908c7cfa3 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -28,7 +28,7 @@ Without ZCP, an app prompt often turns into an operations runbook. With ZCP, you - the service map, runtime target, dev/stage state, or managed-service inventory, - database credentials, private hostnames, env-var references, or generated connection strings, - build logs, runtime logs, event timelines, or a guess about why the last deploy failed, -- boilerplate like "deploy it, verify it, check persistence after refresh, and report the URL", +- a deploy/verify/recovery script for every task; ZCP treats proof or a concrete blocker as part of completion, - a recap after the chat loses context; the agent can read current ZCP status. **You still own the decisions that need human judgment:** product intent, technology constraints, acceptance criteria, external credentials, repository policy, and approval for destructive actions. @@ -43,7 +43,7 @@ To start using ZCP, choose one path: add remote setup to a Zerops project, or in Remote ZCP setup with the zcp service in Zerops and Claude Code running in browser VS Code @@ -55,7 +55,7 @@ To start using ZCP, choose one path: add remote setup to a Zerops project, or in **Zerops token.** ZCP itself connects to Zerops through a project-scoped Zerops token. In remote setup, Zerops injects that token into the `zcp@1` service. In local setup, you provide a project-scoped token when you initialize ZCP beside your editor or CLI agent. Token details live in [Tokens and credentials](/zcp/security/tokens-and-project-access). -**Workspace freedom.** The `zcp@1` service is still a normal project service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and shape the workspace around the bundled setup. ZCP is the project control plane available to the agent; it is not a closed agent product. Details live in [Use remote ZCP workspace](/zcp/setup/hosted-workspace#make-customization-persistent). +**Workspace freedom.** The `zcp@1` service is still a normal project service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and adapt the workspace around the bundled setup. ZCP is the project control plane available to the agent; it is not a closed agent product. Details live in [Use remote workspace](/zcp/setup/hosted-workspace#make-customization-persistent). :::caution Production boundary Use ZCP in development or staging projects. Production should stay in a separate Zerops project and receive promoted work through your CI or release process; see [Production boundary](/zcp/security/production-policy). @@ -76,7 +76,7 @@ Specific tasks and reference: | If you want to | Read | | ---------------------------------------- | --------------------------------------------------------------------------- | -| Use remote workspace in a project | [Use remote ZCP workspace](/zcp/setup/hosted-workspace) | +| Use remote workspace in a project | [Use remote workspace](/zcp/setup/hosted-workspace) | | Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | | Decide how finished work ships | [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | | Understand tokens and project boundaries | [Trust model](/zcp/security/trust-model) | diff --git a/apps/docs/content/zcp/quickstart.mdx b/apps/docs/content/zcp/quickstart.mdx index 775e61a48..862476d89 100644 --- a/apps/docs/content/zcp/quickstart.mdx +++ b/apps/docs/content/zcp/quickstart.mdx @@ -30,11 +30,11 @@ This quickstart uses the [Laravel showcase recipe](https://app.zerops.io/recipes darkImage="/img/zcp/quickstart-laravel-ai-agent-recipe.png" alt="Laravel showcase recipe with the AI Agent environment selected and ZCP configuration enabled" style={{ width: '95%', height: 'auto' }} - caption="The AI Agent environment adds app services plus remote ZCP setup with Coding Agent and Cloud IDE enabled." + caption="The AI Agent environment adds app services plus remote setup with Coding Agent and Cloud IDE enabled." /> -The deploy creates the app runtimes, managed dependencies, and the `zcp@1` workspace service together. The `zcp` service is where the agent, terminal, and browser IDE run. App code still deploys to the app runtime services, not to `zcp`. +The deploy creates the app runtimes, managed dependencies, and a workspace service using the `zcp@1` service type. In this recipe it appears as the `zcp` service. That service is where the agent, terminal, and browser IDE run. App code still deploys to the app runtime services, not to `zcp`. ## 2. Authorize Claude Code @@ -48,9 +48,9 @@ If you close the prompt, open the project in the dashboard and then open the `zc ZCP service panel with Browser VS Code, web terminal, SSH, desktop IDE access, and agent authorized state @@ -68,7 +68,7 @@ After authentication, continue into **Browser VS Code**. The workspace opens wit /> -You are now inside the remote ZCP workspace. The agent can read project state, use project-scoped ZCP operations, reach project-private services, and deploy app changes to the runtime services created by the recipe. +You are now inside the remote workspace. The agent can read project state, use project-scoped ZCP operations, reach project-private services, and deploy app changes to the runtime services created by the recipe. ## 4. Ask for a product outcome @@ -76,27 +76,28 @@ In Claude Code, ask for the app behavior in natural language. A good first promp ```text Build a task board for my team. +Tasks should stay saved after refresh. ``` -Do not add "deploy it", "verify it", or "send me the URL" just to make the agent finish the work. With ZCP, those are part of the expected app task. +The agent is expected to deploy, verify, read evidence, and return proof for the app task. Reserve extra prompt detail for decisions that change the work. Add details when they change the product, stack, runtime layout, acceptance criteria, or delivery path: ```text -Build a Node.js task board with latest PostgreSQL on dev+stage. +In this Laravel app, add a task board backed by the existing PostgreSQL service. +A user can create a task, refresh the page, and still see it. ``` -:::note What these prompts demonstrate - -These short prompts are not the only way to use ZCP, and they are not a claim that ZCP is only for one-shot app generation. They show what ZCP can offload from the user: project discovery, infrastructure fit, Zerops wiring, deploy, verification, and recovery. For normal agentic development, work the way you already work with an agent: describe the next product change, review the result, add constraints when they matter, and let ZCP carry the Zerops infrastructure and integration work behind that loop. +:::note Prompt shape +Short prompts work when they describe the product outcome. Add stack, runtime layout, acceptance criteria, delivery preference, external credentials, or destructive-action approval only when those decisions matter. ZCP carries project discovery, Zerops wiring, deploy, verification, evidence reading, and recovery behind that request. ::: ## 5. Read the proof The final answer should give you a real URL, endpoint, UI state, stored result, or a concrete blocker. Open the URL and try the core behavior the agent says it verified. -If you used the technical prompt above, create a task, refresh the page, and confirm the task is still there. If it disappears, the app is not done; ask the agent to verify that exact behavior again and continue from the project evidence rather than starting over. +For the task-board prompt, create a task, refresh the page, and confirm the task is still there. If it disappears, the app is not done; ask the agent to verify that exact behavior again and continue from the project evidence rather than starting over. If the agent cannot finish, the useful output is a concrete blocker: the missing credential, decision, unsupported runtime choice, or repeated failure it could not recover from. 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 eb711e37c..dbbc87dbf 100644 --- a/apps/docs/content/zcp/security/tokens-and-project-access.mdx +++ b/apps/docs/content/zcp/security/tokens-and-project-access.mdx @@ -14,7 +14,7 @@ Remote setup gets `ZCP_API_KEY` from the Zerops platform. Local setup reads it f | `ZCP_API_KEY` | ZCP against one Zerops project | Remote: `zcp` service env injected by Zerops. Local: `.mcp.json` env block. | | Agent login or model API key | The coding agent itself | The bundled agent in remote setup, or your local agent client. | | `GIT_TOKEN` | Git push from remote setup to a git provider | Secret env var on the `zcp` service when remote git-push delivery needs it. | -| `ZEROPS_TOKEN` | `zcli` in GitHub Actions or external CI | Repository or CI secret for the workflow that deploys to Zerops. | +| `ZEROPS_TOKEN` | `zcli` in GitHub Actions or external CI | Separate project-scoped Zerops token stored in the CI/release secret store. | Keeping these names separate prevents most setup and delivery failures. @@ -55,6 +55,8 @@ Common messages: | `No authentication found: set ZCP_API_KEY or log in with zcli` | The `zcp` process did not receive a usable token. | | `AUTH_TOKEN_EXPIRED` or HTTP 401 | The token expired, was revoked, or is invalid. | +For ZCP setup, provide `ZCP_API_KEY`. `zcli` login is a diagnostic fallback, not the normal agent setup path. + ## Where the token lives - remote vs local {#where-the-token-lives--remote-vs-local} ZCP reads `ZCP_API_KEY` from its process environment at startup. It does not write the token somewhere else or exchange it for a derived credential. @@ -94,12 +96,14 @@ Zerops wires the agent to the project through ZCP. It does not provide your mode `GIT_TOKEN` matters only when remote setup pushes to a git remote. It authorizes git provider access from the `zcp` service. In local setup, your local git CLI uses your normal SSH key or credential helper, so ZCP does not need `GIT_TOKEN`. -`ZEROPS_TOKEN` is a Zerops API token used by GitHub Actions or another CI system when that system runs `zcli` against Zerops. It is not a GitHub token. It can be the same project-scoped token used as `ZCP_API_KEY`, or a separate project-scoped token if your team prefers separate rotation. +`ZEROPS_TOKEN` is a Zerops API token used by GitHub Actions or another CI system when that system runs `zcli` against Zerops. It is not a GitHub token. Use a separate project-scoped delivery token so ZCP sessions and CI/release workflows can be named, rotated, and audited independently. + +For production delivery, `ZEROPS_TOKEN` should be scoped to the production project and stored only in the production CI or release secret store. With GitHub CLI, the secret shape is: ```bash -gh secret set ZEROPS_TOKEN -b "$ZCP_API_KEY" +gh secret set ZEROPS_TOKEN -b "$ZEROPS_DELIVERY_TOKEN" ``` Use the GitHub UI or your CI secret manager instead if your team does not allow local CLI secret writes. @@ -110,7 +114,7 @@ Rotate in the Zerops dashboard, then update the surface that consumes the token. | Surface | Rotation step | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -| Remote `ZCP_API_KEY` | Reconfigure or redeploy remote ZCP through the dashboard-managed path, then restart the `zcp` service so the process gets the new value. | +| Remote `ZCP_API_KEY` | Reconfigure or redeploy the remote workspace through the dashboard-managed path, then restart the `zcp` service so the process gets the new value. | | Local `ZCP_API_KEY` | Paste the new token into `.mcp.json`, then restart the local agent client. | | `ZEROPS_TOKEN` in CI | Replace the repository or CI secret. The next workflow run uses the new value. | | `GIT_TOKEN` | Replace the git-provider credential stored for remote setup. | @@ -152,7 +156,7 @@ Threat model and boundaries: [Trust model](/zcp/security/trust-model). ## Related - [Trust model](/zcp/security/trust-model) - the project boundary this page enforces. -- [Use remote ZCP workspace](/zcp/setup/hosted-workspace) - automatic token injection. +- [Use remote workspace](/zcp/setup/hosted-workspace) - automatic token injection. - [Set up local ZCP](/zcp/setup/local-agent-bridge) - local `.mcp.json` token setup. - [Production boundary](/zcp/security/production-policy) - why production gets separate credentials. - [GitHub integration](/references/github-integration) - CI secret usage with Zerops. diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index ff676bca6..7bc981984 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -70,11 +70,11 @@ Remote setup can start from: - **Recipe with AI Agent environment.** A guided path for a known stack. A recipe creates app services, managed services, and the `zcp@1` workspace together. - **New project with ZCP enabled.** Start from a blank or custom project and add ZCP during project creation. -- **Existing project.** Add the `zcp` workspace next to services that already exist. +- **Existing development or staging project.** Add the `zcp` workspace next to services that already exist. Do not add ZCP to a production project; promote verified work through your release path instead. Local setup can start from: -- **Empty local directory.** The agent creates the app shape from a product request and uses ZCP to select or create Zerops services. +- **Empty local directory.** The agent creates the app structure from a product request and uses ZCP to select or create Zerops services. - **Existing project directory.** The app code, editor setup, local data, and git credentials already live on your machine. - **Recipe prepared for local setup.** The recipe creates the Zerops service baseline, while the agent and files stay local. @@ -101,8 +101,16 @@ You can change setup path later without changing the ZCP project boundary. - Start local for a repo-first project, then add remote setup when you want a project-contained agent workspace. - Keep both paths only when your team understands which source tree is authoritative for deploys. +Before switching, make the handoff explicit: + +- Commit, push, or otherwise preserve the source tree that currently owns deploys. +- Choose one deploy source for the next task: the remote workspace/mounted runtime files, or the local project directory. +- Do not edit mounted runtime files and a local repo in parallel unless you have a merge plan. +- Regenerate local `.env` snapshots after project env changes. +- Relink the local deploy target if the runtime hostname changed. + ## Next steps -- [Use remote ZCP workspace](/zcp/setup/hosted-workspace) +- [Use remote workspace](/zcp/setup/hosted-workspace) - [Set up local ZCP](/zcp/setup/local-agent-bridge) - [Trust model](/zcp/security/trust-model) diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index 6e0706fc6..444991874 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -1,5 +1,5 @@ --- -title: 'Use remote ZCP workspace' +title: 'Use remote workspace' description: 'Use the project-contained zcp@1 workspace with Claude Code, browser VS Code, and project-scoped ZCP access.' --- @@ -24,14 +24,18 @@ For the local alternative, see [Set up local ZCP](/zcp/setup/local-agent-bridge) ## Choose a starting path -### Recipe with AI Agent environment - -Use this when you want a guided stack baseline. A recipe with an **AI Agent** environment creates app services, managed services, and the `zcp@1` workspace together. +### First-time trial -Keep **Coding Agent** enabled when you want bundled Claude Code. Keep **Cloud IDE** enabled when you want Browser VS Code in the project. The dashboard click-through is covered in the [Quickstart](/zcp/quickstart). +Use the [Quickstart](/zcp/quickstart) when you want the guided recipe path. It covers the recipe catalog, **AI Agent** environment, Claude Code authentication, Browser VS Code, first product prompt, and proof. After provisioning, continue with a product prompt in [Build with ZCP](/zcp/workflows/build-with-zcp). +### Recipe with AI Agent environment + +Use this when you want a guided stack baseline for a new development or staging project. A recipe with an **AI Agent** environment creates app services, managed services, and the `zcp@1` workspace together. + +Keep **Coding Agent** enabled when you want bundled Claude Code. Keep **Cloud IDE** enabled when you want Browser VS Code in the project. + ### New project with ZCP enabled Use this when you want a blank or custom project. @@ -45,9 +49,9 @@ Use this when you want a blank or custom project. This gives you a project with the `zcp@1` workspace. App runtimes and managed services may still be created later by normal ZCP app work. -### Existing project +### Existing development or staging project -Use this when the project already has runtime or managed services. +Use this when a development or staging project already has runtime or managed services. Do not add `zcp` to a production project; create or use a separate development/staging project and promote verified work through the release path. Add a `zcp` service from the project dashboard the same way you add another Zerops service. The workspace appears next to the existing services and receives project-scoped ZCP access from the platform. @@ -55,14 +59,14 @@ The agent should still read project state before changing anything. Existing ser ## Open the workspace -After the `zcp` service is running: +For a returning remote workspace, open the existing `zcp` service: 1. Open the project in the [Zerops dashboard](https://app.zerops.io/). 2. Open the `zcp` service. 3. Use **Browser VS Code** when **Cloud IDE** is enabled. 4. Complete the Claude Code login or API-token flow if prompted. -After authentication, Claude Code is connected to this project's ZCP service and ready to work from the browser VS Code terminal. +After authentication, Claude Code is connected to this project's ZCP service and ready to work from the browser VS Code terminal. If the workspace was already authorized, use this same path to resume or supervise work. First load can take a minute while the image, Cloud IDE, and bundled agent finish starting. If the page opens before the service is ready, wait until the service is running and reload. @@ -76,6 +80,10 @@ This keeps the agent and project-private network inside Zerops while letting you You can also install another agent CLI or additional MCP servers inside the `zcp` service. The bundled Claude Code flow is a convenience, not a closed product boundary. +## Advanced customization guardrails + +Tools installed inside the `zcp` service may see workspace environment variables, project-private networking, and any runtime files mounted into the workspace. Use trusted and pinned tools, and keep model credentials, git delivery tokens, external API keys, and production credentials out of the workspace unless the current task explicitly needs them. + ## What belongs where | Concern | Where it belongs | diff --git a/apps/docs/content/zcp/setup/local-agent-bridge.mdx b/apps/docs/content/zcp/setup/local-agent-bridge.mdx index e549e9507..e94662aa7 100644 --- a/apps/docs/content/zcp/setup/local-agent-bridge.mdx +++ b/apps/docs/content/zcp/setup/local-agent-bridge.mdx @@ -17,7 +17,7 @@ Local setup has more moving parts than remote setup and may change faster. The b Pick the folder that should own app work: -- **Empty local directory.** Start with no app code yet. The agent can create the app shape and use ZCP to select or create Zerops services. +- **Empty local directory.** Start with no app code yet. The agent can create the app structure and use ZCP to select or create Zerops services. - **Existing project directory.** Use this when app code, local data, editor setup, test fixtures, and git credentials already live on your machine. - **Recipe prepared for local setup.** Use a recipe to create the Zerops service baseline, then run the agent locally from the directory that should own source changes. 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 af5a127d3..57c805d31 100644 --- a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx +++ b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx @@ -1,10 +1,11 @@ --- -title: 'Deploy, verify, and fix' +title: 'Moved: Build with ZCP' description: 'Development and verification are now covered in Build with ZCP.' +unlisted: true --- -Development, deploy, verification, and evidence-driven fixes are part of the normal ZCP development lifecycle. The user-facing development controls live in [Build with ZCP → Develop with live project context](/zcp/workflows/build-with-zcp#develop-with-live-project-context). The deeper verification model lives in [How ZCP works → Verification has two layers](/zcp/concept/how-it-works#verification-has-two-layers). +This page moved to [Build with ZCP](/zcp/workflows/build-with-zcp). -Those sections explain how the agent works from live project state, Zerops-specific knowledge, project-scoped tools, deploy evidence, and behavior proof. +Development, deploy, verification, and evidence-driven fixes are part of the normal ZCP development lifecycle. The user-facing development controls live in [Build with ZCP -> Develop with live project context](/zcp/workflows/build-with-zcp#develop-with-live-project-context). The deeper verification model lives in [How ZCP works -> Verification has two layers](/zcp/concept/how-it-works#verification-has-two-layers). For symptoms and recovery moves, use [Troubleshooting](/zcp/reference/troubleshooting). diff --git a/apps/docs/content/zcp/workflows/build-with-zcp.mdx b/apps/docs/content/zcp/workflows/build-with-zcp.mdx index 3acae6916..727579509 100644 --- a/apps/docs/content/zcp/workflows/build-with-zcp.mdx +++ b/apps/docs/content/zcp/workflows/build-with-zcp.mdx @@ -10,9 +10,9 @@ Build a task board. Tasks should stay saved after refresh. ``` -Behind that prompt, ZCP gives the agent a development surface for one Zerops project: choose the runtime layout, develop from current project state and Zerops-specific guidance, then follow the delivery preference you choose. +Behind that prompt, ZCP gives the agent a development surface for one Zerops project: settle the runtime layout when it is unclear, work from live project context and Zerops-specific guidance, then close according to the delivery preference after proof. -You do not need to add "deploy it", "verify it", or "send me the URL" to every prompt. Add words when they change the product, stack, runtime layout, acceptance criteria, or delivery path. +Prompt for the outcome, constraints, acceptance criteria, runtime layout, and delivery preference when they matter. ZCP treats deploy, verification, evidence reading, and proof or blocker reporting as part of completion. ## The lifecycle @@ -24,40 +24,42 @@ You do not need to add "deploy it", "verify it", or "send me the URL" to every p -The important user-facing concepts are the runtime layout, the development inputs ZCP can work from, and the delivery preference. The exact internal labels live in [Workflow terms](/zcp/reference/agent-workflow). +The important user-facing controls are the runtime layout you want, the product or code change you ask for, and the delivery preference that should apply after the work is proven. You do not run the flows by hand; ZCP uses them to give the agent the right context and done criteria. -## 1. Choose the runtime layout {#choose-the-runtime-layout} +## 1. Settle the runtime layout {#choose-the-runtime-layout} -The runtime layout decides where the agent should build app code. You can let ZCP infer it from the project, or you can name the shape in the prompt when it matters. +When the project does not yet have a clear app runtime and dependency set, ZCP prepares the project layout before app feature work starts. It reads current project state, uses existing services when they fit, creates missing app runtimes or managed services when needed, and stops when the agent knows where app code belongs. + +The user-facing choice that most affects this first step is the runtime layout. You can let ZCP infer it from the project, or you can name the layout in the prompt when it matters. | Runtime layout | Use when | What to tell the agent | | ------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------- | @@ -65,13 +67,17 @@ The runtime layout decides where the agent should build app code. You can let ZC | **Dev + stage** | You want a development runtime plus a separate runtime for review or promotion rehearsal. | `Build a Node.js API with PostgreSQL on dev+stage.` | | **Stage / linked target** | You work from local files or a single target runtime and want ZCP to use that target. | `Use appstage as the deploy target for this local app.` | +If you do not specify a layout, the agent should infer from the existing project and ask only when the choice changes cost, runtime layout, stage scope, credentials, or a destructive action. + The layout is about app runtimes, not where ZCP itself runs. Remote setup can use any of these layouts from the `zcp@1` workspace. Local setup usually works from local files into a linked stage or app runtime. Managed services such as PostgreSQL, Valkey, queues, search, storage, or mail are dependencies. ZCP gives the agent their state and wiring patterns, but app code deploys to runtime services. ## 2. Develop with live project context {#develop-with-live-project-context} -This is the core value of ZCP during development: the agent does not work from a stale prompt or copied log snippet. It gets current project state, Zerops-specific knowledge, and workflow instructions scoped to the project in front of it. +Any instruction that implies interaction with code starts or continues app development: build, inspect, change, fix, continue, deploy, or verify app work. ZCP wraps that work with the project context the agent needs, but it does not replace the normal coding conversation between you and the agent. + +Inside app development, the agent can still read files, make implementation choices, run tools, and follow your product instructions. ZCP matters around that work: it supplies current project state, relevant Zerops knowledge for the services in the project, scoped operations, deploy evidence, verification checks, and recovery rules. | What the agent gets from ZCP | Why it matters during development | | ---------------------------- | ------------------------------------------------------------------------------------------------------- | @@ -82,18 +88,23 @@ This is the core value of ZCP during development: the agent does not work from a That means you usually do not paste an infrastructure inventory, env wiring plan, or log summary into the chat. A short product prompt can be enough because the agent can ask ZCP what exists now. -What you control here is the product request, stack preference, acceptance criteria, and any instruction to inspect or continue existing work. ZCP handles the deploy, verification, evidence reading, and recovery loop behind that request. +What you control here is the product request, stack preference, acceptance criteria, and any instruction to inspect or continue existing work. ZCP gives the agent the deploy, verification, evidence reading, and recovery loop behind that request. + +Expect the agent to stop for external credentials, destructive actions, cost-affecting infrastructure, ambiguous runtime or stage choices, and product decisions it cannot infer. | Development input | Use when | What to tell the agent | | ----------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------- | | **Product behavior** | The feature or app outcome matters more than the exact stack. | `Build a task board where tasks stay saved after refresh.` | | **Stack preference** | You want a specific runtime, framework, or managed service. | `Use Laravel and the existing PostgreSQL service.` | | **Acceptance criteria** | A specific behavior must be true before the task is done. | `A user can create a task, refresh the page, and still see it.` | +| **Runtime layout** | The target layout matters for the work or review path. | `Build a Node.js API with PostgreSQL on dev+stage.` | | **Existing work** | A previous ZCP session stopped or the project already exists. | `Read ZCP status first, then continue the interrupted task board work.` | ## 3. Choose delivery preference {#choose-delivery-after-proof} -Delivery preference controls how ZCP should finish work and how future ZCP sessions should ship changes. You can include it in the original prompt or set it later. +Delivery preference is how app work closes after there is a verified result. You can include it in the original prompt or set it later. + +The first functional deploy is still direct so the agent can prove the app actually runs. Delivery preference decides what happens after that proof and how later ZCP sessions should finish similar work. | Delivery preference | What it means | What to tell the agent | | ---------------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | @@ -118,3 +129,11 @@ For a completed app task, the agent should report: If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the decision or credential needed from you. Advanced reuse path: [Package a running service](/zcp/workflows/package-running-service) turns a verified runtime into a re-importable bundle. It is not the normal lifecycle for the next app change. + +## Next steps + + diff --git a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx index 40b6ca4cd..9472fa725 100644 --- a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx +++ b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx @@ -1,10 +1,11 @@ --- -title: 'Set up app services' +title: 'Moved: Build with ZCP' description: 'Infrastructure setup is now covered in Build with ZCP.' +unlisted: true --- -Infrastructure setup is part of the normal ZCP development lifecycle. The current guide lives in [Build with ZCP → Choose the runtime layout](/zcp/workflows/build-with-zcp#choose-the-runtime-layout). +This page moved to [Build with ZCP](/zcp/workflows/build-with-zcp). -That section explains how ZCP helps the agent choose between dev, dev+stage, or a stage/linked target layout, reuse existing services, create missing runtimes or managed services, and move into app work once the project shape is clear. +Infrastructure setup is part of the normal ZCP development lifecycle. The current guide explains how ZCP helps the agent choose between dev, dev+stage, or a stage/linked target layout, use existing services, create missing runtimes or managed services, and move into app work once the project layout is clear. For exact route names and workflow vocabulary, use [Workflow terms](/zcp/reference/agent-workflow). diff --git a/apps/docs/content/zcp/workflows/delivery-handoff.mdx b/apps/docs/content/zcp/workflows/delivery-handoff.mdx index 36a32e5ad..5b60a049f 100644 --- a/apps/docs/content/zcp/workflows/delivery-handoff.mdx +++ b/apps/docs/content/zcp/workflows/delivery-handoff.mdx @@ -1,10 +1,11 @@ --- -title: 'Choose how finished work ships' +title: 'Moved: Build with ZCP' description: 'Delivery preference is now covered in Build with ZCP.' +unlisted: true --- -Delivery preference is part of the normal ZCP development lifecycle. The current guide lives in [Build with ZCP → Choose delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). +This page moved to [Build with ZCP](/zcp/workflows/build-with-zcp). -That section explains the three user-facing choices: keep direct deploy, push verified work to git, or hand off to CI/release tooling. ZCP records the delivery choice so later work can follow the same path without re-explaining the handoff every time. +Delivery preference is part of the normal ZCP development lifecycle. The current guide explains the three user-facing choices: keep direct deploy, push verified work to git, or hand off to CI/release tooling. ZCP records the delivery choice so later work can follow the same path without re-explaining the handoff every time. For exact internal labels, use [Workflow terms](/zcp/reference/agent-workflow#delivery-modes). diff --git a/apps/docs/sidebars.js b/apps/docs/sidebars.js index 2c68efdc0..9cd85397e 100644 --- a/apps/docs/sidebars.js +++ b/apps/docs/sidebars.js @@ -626,12 +626,12 @@ module.exports = { { type: 'doc', id: 'zcp/setup/hosted-workspace', - label: 'Remote workspace', + label: 'Use remote workspace', }, { type: 'doc', id: 'zcp/setup/local-agent-bridge', - label: 'Local setup', + label: 'Set up local ZCP', }, ], }, diff --git a/apps/docs/src/css/custom.css b/apps/docs/src/css/custom.css index c8b7909dc..e37233c5d 100644 --- a/apps/docs/src/css/custom.css +++ b/apps/docs/src/css/custom.css @@ -277,6 +277,37 @@ html[data-theme='dark'] .docsearch-btn:hover { } } +@media (max-width: 767px) { + html[class*='docs-doc-id-zcp'] article, + html[class*='docs-doc-id-features/coding-agents'] article, + html[class*='docs-doc-id-features/local-remote-development'] article, + html[class*='docs-doc-id-zcp'] .theme-doc-footer, + html[class*='docs-doc-id-features/coding-agents'] .theme-doc-footer, + html[class*='docs-doc-id-features/local-remote-development'] .theme-doc-footer { + overflow-x: hidden; + } + + html[class*='docs-doc-id-zcp'] article pre, + html[class*='docs-doc-id-features/coding-agents'] article pre, + html[class*='docs-doc-id-features/local-remote-development'] article pre, + html[class*='docs-doc-id-zcp'] article table, + html[class*='docs-doc-id-features/coding-agents'] article table, + html[class*='docs-doc-id-features/local-remote-development'] article table { + max-width: 100%; + overflow-x: auto; + } + + html[class*='docs-doc-id-zcp'] .theme-doc-footer a, + html[class*='docs-doc-id-features/coding-agents'] .theme-doc-footer a, + html[class*='docs-doc-id-features/local-remote-development'] + .theme-doc-footer + a { + max-width: calc(50% - 0.25rem); + min-width: 0; + overflow-wrap: anywhere; + } +} + .zcp-lifecycle-diagram { margin: 1.5rem 0 1.75rem; } diff --git a/apps/docs/static/img/zcp/quickstart-zcp-service-ready.png b/apps/docs/static/img/zcp/quickstart-zcp-service-ready.png index bae7d9514e182c6d2c467d6fd218eea02a205d1e..e3c6413827caa378a85f4a2e7b8bb6283ecf51bd 100644 GIT binary patch literal 268213 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�wq)?dJdrM;o^S7su<~gBQ^su`} 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

pTssx6h_^ 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 Oe+n|H()kaJU;IBkA)$%@ literal 37871 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 Fe*=me6qNt~ diff --git a/apps/docs/static/llms-full.txt b/apps/docs/static/llms-full.txt index 9262f13f7..e43bd52c2 100644 --- a/apps/docs/static/llms-full.txt +++ b/apps/docs/static/llms-full.txt @@ -6349,54 +6349,63 @@ ZCP runs with a project-scoped token that grants operational rights inside that ::: ## Who it's for Developers using a coding agent on application work that needs real infrastructure. The developer stays focused on the product outcome, technology choices, acceptance criteria, and judgment. ZCP gives the agent the platform context and operations needed to use or create services, connect the app to infrastructure, deploy, verify, recover from failure, and report back. -The **Include Coding Agent** option bundles the currently supported agent CLI shown in the dashboard. Local setup can connect a compatible local agent client after `zcp init`. +The **Include Coding Agent** option currently bundles Claude Code and wires it to ZCP. The agent account remains yours: authenticate it with your own subscription login or API credentials. Additional bundled agents are planned. Local setup can connect a compatible local agent client after `zcp init`. ## What ZCP lets the agent do ZCP exposes a fixed set of operations on one Zerops project, grouped by user job. The prompt can stay focused on the app because the agent does not need you to describe what the project looks like - ZCP reports it from what is 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 | [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) | -| **Verify** | Reachability + behavior checks against the actual URL | [Deploy, verify, and fix](/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) | -| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | -| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | +| 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 with ZCP](/zcp/workflows/build-with-zcp#develop-with-live-project-context) | +| **Verify** | Reachability + behavior checks against the actual URL | [How ZCP works](/zcp/concept/how-it-works#verification-has-two-layers) | +| **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) | +| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | +| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | Behind one app request, the agent should read the project, use existing services or create missing ones, edit and deploy the app, verify the requested behavior, and then record how future changes should ship. A green deploy with a 500 on the relevant route is not done — verification has two layers and both must pass. Full workflow terms: [Workflow terms](/zcp/reference/agent-workflow). ## Two ways to run it The `zcp` binary can run in a Zerops service or on your laptop. The user choice is remote setup or local setup. - A Zerops service (`zcp@1`) inside the project runs ZCP. Enable **Include Coding Agent** to add the bundled agent CLI and preconfigure it to use ZCP. Enable Cloud IDE when you want browser VS Code. **Recommended starting point.** [Set up →](/zcp/setup/hosted-workspace) - The `zcp` binary on your laptop, initialized in a project folder with `zcp init`. The agent runs in your local editor or CLI; private service access uses `zcli vpn up`. [Set up →](/zcp/setup/local-agent-bridge) +**Remote setup** runs ZCP inside a Zerops `zcp@1` service. Enable **Include Coding Agent** for the bundled agent CLI, and **Cloud IDE** for Browser VS Code. This is the recommended starting point when broad agent permissions can stay inside the project. [Use remote workspace](/zcp/setup/hosted-workspace) +**Local setup** runs the `zcp` binary on your laptop after `zcp init`. The agent runs in your local editor or CLI; private service access uses `zcli vpn up`. Use it when local files, tools, data, or a local-only agent client should stay in charge. [Set up local ZCP](/zcp/setup/local-agent-bridge) Either way, the agent gets the same project-scoped ZCP operations against the same project. Remote setup can package ZCP with an agent CLI and browser editor; local setup connects your own agent client to ZCP. -Decision: [Choose remote or local setup](/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). +Decision: [Remote or local ZCP setup](/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 remote setup adds -Remote setup starts with a `zcp@1` Zerops service. Two dashboard options decide what is bundled into that service: -**Include Coding Agent.** Adds the currently supported bundled agent CLI and preconfigures it to use ZCP running inside the `zcp@1` service. Agent authentication is handled separately by that agent's login method. -**Cloud IDE.** Browser-based VS Code (code-server) with 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 → Cloud IDE](/features/local-remote-development#cloud-ide-on-remote-setup). -Without **Include Coding Agent**, the service can still exist, but there is no preconfigured agent waiting inside it. Without **Cloud IDE**, there is no browser editor. With both enabled, you get a one-click agent + IDE setup inside the project. -Both options are configured when you provision the service: [Set up remote ZCP → Path B](/zcp/setup/hosted-workspace#path-b--toggle-the-zcp-service). +Remote setup starts with a `zcp@1` Zerops service. **Include Coding Agent** adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use ZCP. **Cloud IDE** adds Browser VS Code so you can supervise or take over from the dashboard. +Without **Include Coding Agent**, there is no preconfigured agent waiting inside the service. Without **Cloud IDE**, there is no browser editor. Configure both when you provision the service if you want the first-run agent + IDE path. Details: [Use remote workspace](/zcp/setup/hosted-workspace#what-you-get). +Remote setup is still your workspace. You can install another agent CLI, add private MCP servers or helper processes, edit `CLAUDE.md` or other agent instruction files, and keep team tooling in the service. ZCP supplies the project-scoped control plane, operations, and workflow evidence; it does not lock the service to one agent or one set of tools. ## 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. +- Resource allocation and scaling limits are queryable. When a change affects cost, the agent can surface the proposed resource change and ask 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. ZCP is the ergonomic layer on top of Zerops APIs, `zcli`, and SSH for agents. It tightens the loop by packaging project state, guidance, operations, and verification behind one project-scoped interface. ## Operations and permissions -ZCP groups its operations into three categories so an agent client or team policy can gate them: - Inspect state, logs, events, configuration, and verification output. Safe to auto-allow. [Full list →](/zcp/reference/mcp-operations#read-only-operations) - Deploy, change env vars, manage lifecycle, scale, or delete services. Require team policy and confirmation where needed. [Full list →](/zcp/reference/mcp-operations#mutating-operations) - Set up infrastructure, mounts, imports, work sessions, or delivery configuration. Gated by team policy. [Full list →](/zcp/reference/mcp-operations#operational-setup) +ZCP groups its operations so an agent client or team policy can gate them: +- **Read-only operations** inspect state, logs, events, configuration, and verification output. [Full list](/zcp/reference/mcp-operations#read-only-operations) +- **Mutating operations** deploy, change env vars, manage lifecycle, scale, or change public access. They are governed by token permissions and your agent/team policy. [Full list](/zcp/reference/mcp-operations#mutating-operations) +- **Destructive operations** have extra ZCP gates: service deletion needs explicit same-conversation approval naming the service, and destructive replacement after failed deploy history requires evidence review first. [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) +- **Operational setup** covers infrastructure, mounts, imports, work sessions, and delivery configuration. [Full list](/zcp/reference/mcp-operations#operational-setup) That's the surface. The boundary is the project. The token authorizes one project, full stop — cross-project, account-wide, or organization actions stay outside reach. Integration tokens are still bounded by the permissions of the user who created them and by the project access selected for the token; see [Roles & Permissions](/features/rbac). Full blast-radius framing: [Trust model](/zcp/security/trust-model). Full operation list and permission semantics: [Advanced operations](/zcp/reference/mcp-operations). Token shape and confirmation gates: [Tokens and credentials](/zcp/security/tokens-and-project-access). ## Customize remote setup -Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product - install packages, run additional processes alongside the agent, or ship a hardened version with team-standard tools baked in. Patterns: [Customize remote setup](/zcp/setup/hosted-workspace#customize-remote-setup). -Local setup isn't extensible the same way; the binary on your laptop is just ZCP - editor, shell, and dev server stay your tools' job. +Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product. Install trusted tools, edit agent instructions, configure additional MCP servers, or ship a hardened version with team-standard tools baked in. Patterns and guardrails: [Make customization persistent](/zcp/setup/hosted-workspace#make-customization-persistent). +Local setup isn't extensible the same way; the binary on your laptop is just ZCP - editor, shell, local files, local data, and tool feedback stay on your machine. ## Source control -When the agent runs in remote setup, 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. In local setup, 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 remote setup, git access belongs inside the workspace: GitHub CLI login, a fine-grained git token stored on the `zcp` service, or SSH agent forwarding from a remote editor. In local setup, it uses your existing local git credentials. Detail: [Tokens and credentials -> Git and CI credentials](/zcp/security/tokens-and-project-access#git-and-ci-credentials). ## What ZCP is not ZCP is not a prompt-to-prototype generator, an isolated code sandbox, or an editor-only cloud development environment. It is the control plane that gives a coding agent project-scoped infrastructure operations on Zerops: services, env vars, deploys, logs, verification, recovery, and delivery handoff. +## How it differs from adjacent tools +ZCP is closest to the tools that let an agent work on real software over time, but the center of gravity is different: the agent operates one real Zerops project rather than a preview sandbox or editor-only workspace. +| Category | What it optimizes for | Where ZCP differs | +| ------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| Prompt-to-prototype tools | Fast artifact generation and visual preview. | ZCP targets a running app on real services, with deploy, logs, persistence, and verification. | +| Code execution sandboxes | Safe isolated execution. | ZCP gives the agent managed services, private networking, deployment, and project state that persist across sessions. | +| AI-enabled cloud IDEs | Editing code in a hosted development environment. | ZCP gives the agent platform operations too: create/use services, wire env vars, deploy, verify, recover, and hand off. | +| Primitive/API collections | Calling many specialized APIs. | ZCP keeps the app and dependencies in one Zerops project, reachable by service names and standard platform mechanics. | +| Primitive/API collections | Calling many specialized APIs. | ZCP keeps the app and dependencies in one Zerops project, reachable by service names and standard platform mechanics. | +The claim is not that ZCP replaces those tools. It solves the missing operational layer when the output must become a verified app running on Zerops infrastructure. ## Where to start ---------------------------------------- @@ -6890,15 +6899,15 @@ Containers are the most granular level of the Zerops architecture. Each service 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. Across local VPN, Cloud IDE, and SSH, you use the same project network, hostnames, service types, and deploy pipeline. This is also what narrows the gap between development and production. Development and staging can use the same Zerops-managed service types, networking model, and `zerops.yaml` patterns production uses. Production should still live in a separate project with separate credentials, access rules, and resource policies. :::note -Remote setup is a `zcp@1` service you can add to any project. It can provide a Linux dev container with a curated toolchain, optional Cloud IDE, and optional **Include Coding Agent** wiring. The modes below can use that service, connect directly to runtime services, or stay fully local over VPN; local development with `zcli vpn up` does not require ZCP at all. +Remote setup is a `zcp@1` service you can add to a development or staging project. It can provide a Linux dev container, optional Cloud IDE, and optional **Include Coding Agent** wiring. The paths below can use that service, connect directly to runtime services, or stay fully local over VPN; local development with `zcli vpn up` does not 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. +The development question is just: how do you, the developer, get onto that network? Three answers give you the same project-private network reach, with different filesystem, tooling, and credential boundaries. - **Local + VPN.** Your laptop joins the private network via WireGuard. Editor, toolchain, and processes stay where they are. Remote setup isn't required. - **Cloud IDE.** 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 the `zcp` service, or directly to a service container. -Same `db:5432`, same `api:3000`, same project credentials in all three. Switch modes without changing anything about the development project. +Same `db:5432`, same `api:3000`, same project credentials in all three. Switch paths without changing anything about the development project. ## Local + VPN **Best for:** keeping your existing editor, dotfiles, and toolchain — and offloading just the dependencies that are painful to run locally. ```bash @@ -6909,25 +6918,19 @@ 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: add remote setup, 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 service type as stage or production. -This mode is also where coding agents running on your laptop fit. Without ZCP, a local agent client inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. To let the agent operate the project - discover, deploy, verify, read logs, and manage delivery handoff - set up [local ZCP](/zcp/setup/local-agent-bridge). +This path is also where coding agents running on your laptop fit. Without ZCP, a local agent client inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. To let the agent operate the project - discover, deploy, verify, read logs, and manage delivery handoff - set up [local ZCP](/zcp/setup/local-agent-bridge). **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). ::: ## Cloud IDE on remote setup **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 remote setup (a `zcp@1` service) to a project — see [Set up remote ZCP](/zcp/setup/hosted-workspace) — you get an Ubuntu-based dev container with `zcp`, `zcli`, GitHub CLI, database CLIs, shell utilities, browser automation tools, and SSHFS. Enable **Cloud IDE** for browser-based VS Code; enable **Include Coding Agent** for the bundled agent CLI plus ZCP wiring. -Remote setup can also mount 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 Zerops deploy pipeline. One remote setup service can mount multiple dev services at once, covering the whole stack. +When you add remote setup (a `zcp@1` service) to a development or staging project - see [Use remote workspace](/zcp/setup/hosted-workspace) - you get a project-contained workspace with `zcp`, private-network access, optional Browser VS Code, and optional bundled agent wiring. +Remote setup can also mount runtime service files into the workspace, so you can inspect or edit code that runs in another container without moving it to your laptop. Details live in [Runtime file access](/zcp/setup/hosted-workspace#runtime-file-access). 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.** Remote ZCP isn't on your laptop, so your local SSH keys don't follow it. Two patterns work: +**Source control.** The remote 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 the `zcp` service's secret environment variables; `git` reads it through a credential helper. Either pattern keeps your token in the `zcp` service, not in your project's source. @@ -6939,7 +6942,7 @@ ssh apidev # any service in the project ssh frontenddev ssh zcp # if you provisioned the `zcp` service ``` -In this mode, remote setup 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. Remote setup becomes a *convenience* layer: a single container that carries the toolchain, mounts multiple dev services over SSHFS, and can include `zcli`, Cloud IDE, and coding-agent wiring for the project. If you'd rather connect your editor directly to your runtime services and skip remote setup entirely, that works. +In this path, remote setup 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. Remote setup becomes a convenience layer: a single workspace that can carry tools, runtime file mounts, Cloud IDE, and coding-agent wiring for the project. If you'd rather connect your editor directly to your runtime services and skip remote setup 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 the `zcp` service) uses your laptop's GitHub key over the forwarded agent socket — no token storage required. ```ssh-config # Local ~/.ssh/config @@ -6950,34 +6953,34 @@ Host apidev.zerops :::info SSH access details See the [SSH reference guide](/references/networking/ssh). ::: -## Picking a mode -| | Local + VPN | Cloud IDE | Native IDE over SSH | -|---|---|---|---| -| Editor runs | Local | Browser | Local | -| Toolchain | Local | Remote ZCP | Remote ZCP or service container | -| Dev databases | Hostname via VPN | Native, on private network | Native, on private network | -| Remote ZCP 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 | -| 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 remote setup are hitting the same `db:5432`. -For the agent-driven path, the setup decision (remote `zcp@1` service vs. local `zcp` binary) is on its own page: [Choose remote or local setup](/zcp/setup/choose-workspace). +## Picking a path +| | Local + VPN | Cloud IDE | Native IDE over SSH | +| --------------------- | ------------------ | -------------------------- | --------------------------------- | +| Editor runs | Local | Browser | Local | +| Toolchain | Local | Remote setup | Remote setup or service container | +| Dev databases | Hostname via VPN | Native, on private network | Native, on private network | +| Remote setup 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 | +| Best for | Keeping your setup | Disposable environments | Native UX, lighter laptop | +You're not locked into one path. The same project can support all three without reprovisioning when your source-of-truth handoff is clear. A teammate on Local + VPN and an agent inside remote setup are hitting the same `db:5432`. +For the agent-driven path, the setup decision (remote `zcp@1` service vs. local `zcp` binary) is on its own page: [Remote or local ZCP setup](/zcp/setup/choose-workspace). ## Same architecture, separate production -The development environments above don't approximate production by inventing a different platform. A ZCP development project can run dev and stage services with the same Zerops-managed service types, private networking, load-balancer model, and `zerops.yaml` patterns that production uses in its separate project. Dev, stage, and production differ in resource allocation, access rules, and credentials, not in architecture. +The development environments above don't approximate production by inventing a different platform. A Zerops development/staging project can run dev and stage services with the same Zerops-managed service types, private networking, load-balancer model, and `zerops.yaml` patterns that production uses in its separate project. Dev, stage, and production differ in resource allocation, access rules, and credentials, 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 and stage, you've already tested it against the kind of infrastructure it'll meet in production. The remaining differences are scale, credentials, access policy, and data. This applies whether the developer is human or an agent — see [ZCP for Coding Agents](/features/coding-agents) for the agent case. ## How this differs from cloud IDEs If you've used GitHub Codespaces or Gitpod, Cloud IDE 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 use the same platform model.** 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 model: managed Postgres, private networking, and `zerops.yaml`. Production still lives in its own project with its own credentials and policies. -Remote ZCP 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. +Remote setup 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 - 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 → [ZCP for Coding Agents](/features/coding-agents) -- Set up remote setup → [Set up remote ZCP](/zcp/setup/hosted-workspace) -- Pick remote or local ZCP → [Choose remote or local setup](/zcp/setup/choose-workspace) +- Use remote workspace → [Use remote workspace](/zcp/setup/hosted-workspace) +- Remote or local ZCP setup → [Remote or local ZCP setup](/zcp/setup/choose-workspace) - Term reference → [ZCP Glossary](/zcp/glossary) ---------------------------------------- @@ -25832,284 +25835,492 @@ For advanced configurations or custom requirements: # Zcp > Concept > How It Works -ZCP turns product or app intent into a verified deployed app by giving the coding agent two things it normally lacks: current knowledge of the Zerops project and the tools to change, deploy, inspect, and verify it. -## The model in one pass -Product intent enters a project loop: read current state, decide whether the needed services already exist, change the app and its Zerops wiring, deploy, verify from evidence, and return either proof or a concrete blocker. +ZCP gives a coding agent an operating loop for one Zerops project. The agent starts from live project state, chooses the app runtime and dependencies, makes the application change, deploys through Zerops, verifies real behavior, and then returns proof or a concrete blocker. +The loop is carried by three things: project-scoped tools, Zerops-specific knowledge, and workflow instructions that tell the agent what to inspect, what it can change, when it should ask, and what counts as done. The point is not that you run a workflow by hand. The point is that the agent has a project-aware path behind a product request and can make the important decisions visible while it works. +## The control loop ```mermaid flowchart TD intent["Product intent -Build a task board where tasks stay saved after refresh."] - context["ZCP context -live services, env vars, logs, events, -runtime guidance, available operations"] - target["Project fit -use existing services or create missing ones; -select the app runtime, -not the zcp setup service"] - app["Build the app -code, zerops.yaml, env wiring"] - deploy["Deploy through Zerops"] - verify{"Verify real behavior -endpoint or UI"} - fix["Read evidence -logs, events, checks"] - done["Done -URL, endpoint, or UI proof"] +Build a task board for my team."] + state["Live project state +services, runtime layout, env vars, +logs, events, saved work state"] + scope["Runtime scope +which app service changes"] + setup{"Missing or unsuitable +services?"} + provision["Service setup +use existing services or create +missing runtimes/dependencies"] + appwork["App work +code, zerops.yaml, env refs, +migrations, seeds, framework config"] + deploy["Direct deploy through Zerops"] + reachability{"Runtime reachable? +status, logs, HTTP probe"} + behavior{"Requested behavior works? +endpoint, UI flow, worker result, +persisted state"} + evidence["Evidence +build logs, runtime logs, +events, verify output"] + proof["Proof +URL, endpoint result, +UI state, or stored result"] + delivery["Delivery after proof +keep direct deploy, push to git, +or hand off"] blocker["Blocker -missing credential, decision, -or repeated failure"] - delivery["Future delivery -direct deploy, git push, or CI handoff"] - intent --> context --> target - target --> app --> deploy --> verify - verify -->|fixable| fix --> app - verify -->|verified| done --> delivery - verify -->|needs human| blocker +credential, decision, +unsupported fit, repeated failure"] + intent --> state --> scope --> setup + setup -->|yes| provision --> appwork + setup -->|no| appwork + appwork --> deploy --> reachability + reachability -->|no, fixable| evidence --> appwork + reachability -->|yes| behavior + behavior -->|no, fixable| evidence + behavior -->|yes| proof --> delivery + setup -->|needs decision| blocker + reachability -->|needs human| blocker + behavior -->|needs human| blocker classDef user fill:#f7fbff,stroke:#2d72d9,stroke-width:1.5px,color:#172033; classDef zcpbox fill:#eef7f1,stroke:#32845a,stroke-width:1.5px,color:#172033; classDef work fill:#fffaf0,stroke:#c47f17,stroke-width:1.5px,color:#172033; classDef done fill:#f5f3ff,stroke:#7157d9,stroke-width:1.5px,color:#172033; classDef stop fill:#fff1f2,stroke:#d33f49,stroke-width:1.5px,color:#172033; class intent user; - class context zcpbox; - class target,app,deploy,verify,fix work; - class done,delivery done; + class state,scope zcpbox; + class setup,provision,appwork,deploy,reachability,behavior,evidence work; + class proof,delivery done; class blocker stop; ``` -The important signals are simple: the agent read current project state, named the target runtime, verified the requested behavior, and explained what remains. -## Where ZCP runs -ZCP can run in two places: -| Path | What runs where | What changes for you | -|---|---|---| -| Remote setup | A `zcp@1` service runs the `zcp` binary inside the Zerops project. **Include Coding Agent** adds the bundled agent CLI and preconfigures it to use ZCP; **Cloud IDE** adds browser VS Code. | The work stays inside the project. The agent can use project-private networking and SSHFS mounts for runtime files. | -| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your editor's agent talks to it. | Your checkout and dev server stay local. Managed services are reached over VPN, and `.env` generation bridges project credentials into your local app. | -| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your editor's agent talks to it. | Your checkout and dev server stay local. Managed services are reached over VPN, and `.env` generation bridges project credentials into your local app. | -The project-scoped control plane is the same idea in both paths. The filesystem, network path, deploy source, and safety profile are different. See [Choose remote or local setup](/zcp/setup/choose-workspace). -## What ZCP reads -ZCP reads live project state instead of relying on a long prompt: -- services and whether they are runtime or managed dependencies, -- runtime layout, such as one app runtime, a dev+stage pair, or a local checkout linked to a Zerops runtime, -- service env-var keys and references, -- build/deploy events, runtime logs, and verification results, -- the current work state when a session is interrupted. -Recovery starts from live state. If a session gets confused or interrupted, ask the agent to read ZCP status before changing anything else. -## What counts as done -A task is not done when code is written, or when a build succeeds. A ZCP task is done when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the URL or a concrete blocker. -Workflow path: [Build with ZCP](/zcp/workflows/build-with-zcp). Exact terms and runtime layouts: [Workflow terms](/zcp/reference/agent-workflow). +The loop gives the agent a way to resolve each phase from project evidence instead of from a guessed checklist. It knows where to read current state, which operations are scoped to the project, which Zerops rules apply to app wiring, which evidence to read after deploy, and when the result is proof versus a blocker. +## What "project state" means +ZCP reads the project instead of asking you to paste a service inventory into the prompt. Useful state includes: +- runtime services and managed services, +- whether the app has one runtime, a dev+stage pair, or local files linked to a Zerops runtime, +- env-var keys and Zerops references, +- recent build/deploy events, +- build logs, runtime logs, and verification output, +- saved work state after an interrupted session. +This is why a short prompt can be enough. The agent can ask ZCP what exists, which runtime was last deployed, which checks passed, and where a previous run stopped. +Chat history is not the source of truth. If the agent sounds confused, starts from an old assumption, or a session was interrupted, the recovery move is: +```text +Read ZCP status and tell me where this project stands before changing anything. +``` +## What ZCP coordinates behind the prompt +ZCP is intentionally opinionated about the concerns an agent has to resolve during app work. You usually do not name these concerns in the prompt. ZCP provides them through project state, tools, guidance, and workflow instructions so the agent can work from evidence. +| Concern | What ZCP gives the agent | What that means for you | +| ---------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| Project state | Live services, env-var keys, Zerops references, recent events, logs, verification output, and saved work state. | You do not have to paste a service inventory or reconstruct what happened. | +| Runtime target | A model for choosing the app runtime that should receive code changes. | The agent can identify where app work belongs before editing or deploying. | +| Managed services | Knowledge and env wiring patterns for database, cache, queue, search, storage, mail, and similar services. | Product intent can imply real dependencies without a manual wiring checklist. | +| Service setup | Project-scoped operations for using existing services or creating missing runtimes and dependencies. | Runtime layout and dependencies can be established before app work starts. | +| App wiring | Zerops-specific rules for `zerops.yaml`, env references, ports, commands, public access, and build/deploy behavior. | The app is wired for Zerops rather than for a generic cloud template. | +| Deploy evidence | Build logs, runtime logs, platform events, service status, HTTP checks, and structured verification output. | A failed deploy becomes a diagnosis surface instead of guesswork. | +| Behavior proof | A done gate based on the requested behavior, not only on a successful build or reachable root URL. | The final answer can point to what was actually checked. | +| Delivery handoff | A workflow contract for direct proof first, then git push, CI, or human handoff when that is the chosen path. | Shipping setup follows a verified running result. | +| Delivery handoff | A workflow contract for direct proof first, then git push, CI, or human handoff when that is the chosen path. | Shipping setup follows a verified running result. | +The exact labels for layouts, delivery modes, and setup routes live in the [Glossary](/zcp/glossary) and [Workflow terms](/zcp/reference/agent-workflow). +## Service setup prepares the project layout +Before app code work starts, ZCP helps the agent make three decisions visible: +- Which runtime service is the app target? +- Which managed services are dependencies? +- Does the existing project layout fit the request? +If the services already exist, the agent can use them. If a needed runtime or dependency is missing, ZCP can create it through project-scoped operations. If the choice affects cost, product scope, credentials, a destructive action, or which stage/runtime should be used, the workflow tells the agent to stop and ask. +Service setup is finished when the app runtime and dependencies are known. It is not the finished app; it is the project layout that lets app work happen in the right place. +## App work changes code and platform wiring together +After the runtime scope is known, ZCP gives the agent the platform knowledge needed to make the application change. In practice this often spans both source code and Zerops wiring: +- app files, +- `zerops.yaml` build and run setup, +- env references to managed services, +- migrations, seeds, and framework config, +- start commands, ports, and public HTTP support, +- local `.env` generation when using local setup. +ZCP guidance matters because Zerops is its own platform. Service references, build/deploy behavior, public access, scaling, and env resolution do not follow Docker Compose or Kubernetes conventions. The agent gets Zerops-specific rules instead of relying on a generic cloud template. +The first functional deploy goes through ZCP's direct deploy path. That deploy creates the running result the agent can verify. A repository push or CI handoff can follow, but it should not replace the first proof. +## Recovery is evidence-driven +When something fails, ZCP gives the agent the evidence surface that matches the failure: +| Failure pattern | Useful evidence | +| ------------------------ | ------------------------------------------------------------------------------------------------------------ | +| Build failed | Build logs, build commands, dependency manifests, deploy file list. | +| Runtime failed to start | Runtime or prepare logs, start command, ports, env references. | +| Route or behavior failed | Verify output, HTTP response, runtime logs at request time, stored state. | +| Network path failed | VPN, SSH, DNS, subdomain readiness, service status, named transport error. | +| Config was rejected | Field-level rejection, `zerops.yaml`, setup name, env reference, service settings. | +| Credential failed | The named credential surface: Zerops token, git token, SSH, managed-service credential, or external API key. | +| Credential failed | The named credential surface: Zerops token, git token, SSH, managed-service credential, or external API key. | +Retrying the same deploy without new evidence is not progress. The workflow pushes the agent toward one of three outcomes: fix from a cause, ask for the missing decision, or report a blocker with the category, evidence read, and attempts made. +## Verification has two layers +A successful deploy proves that Zerops accepted the build and started the runtime. Platform reachability checks prove the service is running and reachable. They still do not prove the product request. +For a task-board app, useful behavior proof might be: +- create a task, +- move it between columns, +- refresh the page, +- confirm the task is still there. +For an API task, proof might be a JSON response from the requested endpoint and stored data behind it. For a worker task, proof might be a processed job and the resulting database or object-storage state. For a staging request, proof belongs on the stage runtime, not only on dev. +The final answer should make that proof inspectable: runtime name, URL or endpoint, behavior checked, and delivery choice if one was set. +## Delivery happens after proof +Delivery choice controls how future changes ship after a verified runtime exists: +| Choice | Use when | +| ------------------ | ------------------------------------------------------------------------------ | +| Keep direct deploy | Early development, demos, dev/stage iteration, or agent-owned runtime changes. | +| Push to git | You want commits, review, repository history, or a repo-triggered build. | +| External handoff | CI, release management, or a human owns the next deploy. | +| External handoff | CI, release management, or a human owns the next deploy. | +Packaging a running service is a separate advanced reuse path. It turns a deployed runtime into a re-importable bundle for another Zerops project. It is useful for handoff or reuse, not for deploying the next app change; see [Package a running service](/zcp/workflows/package-running-service). +## Remote and local setup use the same loop +The same `zcp` binary can run in two places. The control loop stays the same; the filesystem and network path change. +| Path | What runs where | Practical effect | +| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Remote setup | A `zcp@1` service runs the `zcp` binary inside the Zerops project. **Include Coding Agent** adds the bundled agent CLI; **Cloud IDE** adds browser VS Code. | Work happens inside the project boundary. The agent can use project-private networking and runtime file mounts. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your local editor or CLI agent talks to it. | App files, local data, deploy source, and git credentials stay on your machine. Managed services are reached over Zerops VPN, and `.env` generation bridges project credentials into your local app. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your local editor or CLI agent talks to it. | App files, local data, deploy source, and git credentials stay on your machine. Managed services are reached over Zerops VPN, and `.env` generation bridges project credentials into your local app. | +Base the setup path on where you want the agent and filesystem to live: [Remote or local ZCP setup](/zcp/setup/choose-workspace). +## Signs of a healthy ZCP run +A well-shaped run should: +- name the runtime scope before editing or deploying, +- use existing services when they fit, +- create missing services only before app work starts, +- read logs, events, and verify output when failure occurs, +- distinguish runtime reachability from requested behavior, +- stop before destructive actions, ambiguous runtime/stage choices, or missing credentials, +- end with proof or a concrete blocker. +That is the practical difference between "the agent wrote code" and "the app task is done". +## Where to go deeper +- [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. +- [Workflow terms](/zcp/reference/agent-workflow) - exact runtime layouts, delivery terms, failure categories, and completion evidence. +- [Troubleshooting](/zcp/reference/troubleshooting) - practical recovery when a run gets stuck. ---------------------------------------- # Zcp > Glossary +Use the exact labels here when you are reading ZCP status, writing agent policy, debugging a session, or reviewing a handoff. In normal prompts, describe the outcome you want. ## Core names **ZCP** - Zerops Control Plane for coding agents: the project-scoped control plane documented in this section. -**ZCP operations** - the project-scoped operations surface exposed to a coding agent. In MCP clients, this appears as the ZCP MCP server. -**`zcp` binary** - the executable. Runs inside remote setup or on your laptop in local setup. +**ZCP operations** - the project-scoped operations surface exposed to a coding agent. In MCP clients, this usually appears as the `zerops` server. +**MCP server** - the Model Context Protocol server that exposes ZCP operations to an agent client. +**Agent client** - the editor, CLI, or hosted agent runtime that connects to ZCP operations. +**`zcp` binary** - the executable. It runs inside remote setup or on your laptop in local setup. +**`zcp` service** - the service instance in a Zerops project that hosts remote setup. +**`zcp@1` service** - the Zerops service type behind remote setup. **zCLI** - the Zerops command-line client for humans, scripts, VPN, and CI. It coexists with ZCP. **zsc** - the in-container Zerops Setup Control utility used from `zerops.yaml`. ## Where ZCP runs **Remote setup** - ZCP running inside a Zerops `zcp@1` service in the project. -**`zcp@1` service** - the Zerops service type behind remote setup. -**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI and preconfigures it to use the project's ZCP operations. +**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use the project's ZCP operations. **Cloud IDE** - browser-based VS Code served by remote setup. +**Browser VS Code** - the dashboard entry point into the Cloud IDE. **AI Agent environment** - a recipe environment preset that creates app services plus remote setup with **Include Coding Agent** enabled. It is a recipe preset, not a separate ZCP setup path. **Local setup** - the `zcp` binary running on your laptop after `zcp init`, while your editor or CLI agent talks to it. **Local agent bridge** - local setup with the `zcp` binary connected to a local agent client. -## Workflows and results +**Env bridge** - local setup behavior that writes a local `.env` snapshot from project env vars and Zerops references so local app code can reach managed services over VPN. +## Work phases and loops **Service setup** - the infrastructure phase behind an app prompt. It uses existing runtime and managed services when they fit, creates missing services when needed, then stops before app code, `zerops.yaml`, or deploy. -**Deploy, verify, and fix** - the app phase. It scopes the runtime, changes code/config, deploys, verifies reachability, verifies requested behavior, and fixes failures. +**Infrastructure setup** - user-facing synonym for service setup. +**Setup route** - the exact label for how service setup starts: +- `adopt` - runtime services already exist and should be used. +- `recipe` - an empty or ZCP-only project matches a known recipe/stack. +- `classic` - an empty project needs a custom service plan. +- `resume` - an interrupted setup should continue from saved state and live services. +**Deploy, verify, and fix** - the app phase. It scopes the runtime, changes code/config, deploys, verifies reachability, verifies requested behavior, and fixes failures from evidence. +**Develop flow** - exact reference name for the deploy, verify, and fix loop. **Runtime scope** - the app runtime the change targets, such as `appdev`, `appstage`, `app`, or a linked local target. -**Delivery choice** - what happens to future changes after a verified deploy: direct deploy, git-push, or external handoff. +**Direct deploy** - a ZCP deploy from the current source to the scoped runtime. The first verified running result uses direct deploy before delivery setup is applied. +**Reachability verification** - checks that the runtime exists, is running, has no recent blocking errors, and can answer an HTTP probe when it is an HTTP service. +**Behavior verification** - checks that the user-requested app behavior works on the real URL, endpoint, worker result, or stored state. +**Completion evidence** - the runtime, deploy, reachability check, behavior check, URL/endpoint/UI/state proof, and delivery choice or blocker that justify calling the task done. +**Proof** - user-inspectable completion evidence, such as a URL, endpoint result, UI state, processed job, or stored result. **ZCP status** - a live project read the agent can use after interruption or confusion. Ask for it before letting the agent change anything else. **Blocker** - a clear stop state with evidence: failure category, runtime in scope, what was tried, and what decision or credential is needed. +## Delivery and reuse +**Delivery choice** - what happens to future changes after a verified deploy: direct deploy, git-push, or external handoff. +**Delivery mode** - exact reference label for delivery choice: +- `auto` - keep direct deploy for future changes. +- `git-push` - commit and push to a configured remote, then observe/verify any tracked build. +- `manual` - external CI, release process, or a human owns future delivery. +**Git-push capability** - whether ZCP has enough remote URL and credential setup to push from remote setup. It can exist even when the current delivery mode is `auto`. +**Build integration** - repository-triggered build/deploy path that ZCP may configure or observe, such as a Zerops dashboard webhook or GitHub Actions. It is separate from git-push capability and delivery mode. +**External handoff** - delivery mode where ZCP records proof and state, but a human or external system owns future deploys. +**Package a running service** - advanced reuse workflow that turns one deployed runtime and its managed dependencies into a re-importable bundle. It is not the normal path for deploying the next app change. +**Packaging env bucket** - exact labels used when classifying project env vars during packaging: +- `infrastructure` - value comes from a managed-service reference and should be regenerated by the new project. +- `auto-secret` - local app secret that should be freshly generated on re-import. +- `external-secret` - third-party API key or credential that becomes a `REPLACE_ME` placeholder. +- `plain-config` - literal non-secret config copied into the bundle. ## Project and runtime terms **Runtime service** - a service that runs app code. -**Managed service** - database, cache, queue, search, storage, or similar dependency. It provides connection details; it is not an app deploy target. +**Target runtime service** - the specific runtime service selected for the current app change. +**Managed service** - database, cache, queue, search, storage, mail, or similar dependency. It provides connection details; it is not an app deploy target. **Runtime layout** - which app runtime services ZCP should use: - `standard` - dev runtime plus explicit stage runtime. - `dev` - one mutable development runtime. - `simple` - one runtime with no dev/stage split. -- `local-stage` - local checkout linked to one Zerops runtime as deploy target. -- `local-only` - local checkout with no linked runtime yet. +- `local-stage` - local source directory linked to one Zerops runtime as deploy target. +- `local-only` - local source directory with no linked runtime yet. +**Dev runtime** - mutable runtime used for iterative development. +**Stage runtime** - review or promotion target. Stage is not production. **Service scaling mode** - Zerops scaling setting such as `HA` or `NON_HA`. Different from runtime layout. -**Stage** - review or promotion target. Stage is not production. +**Public subdomain access** - Zerops public URL access for an eligible HTTP runtime. Workers and non-HTTP services do not get useful public URLs. +## Failures and recovery +**Failure category** - exact label that points to the first useful evidence surface: +- `build` - build phase failed. +- `start` - build passed, but runtime start or prepare failed. +- `verify` - runtime exists, but reachability or behavior failed. +- `network` - transport, DNS, VPN, SSH, or service-to-service reach failed. +- `config` - `zerops.yaml`, env vars, setup block, or service settings mismatch. +- `credential` - Zerops, git, SSH, managed-service, or external API credential failed. +- `other` - no known category matched. +**Recovery hint** - structured next move surfaced by ZCP when the failure has an actionable recovery path. +**Confirmation gate** - an operation that pauses until the user explicitly confirms the named target or consequence. +**Destructive import override** - import action that would replace an existing service stack. ZCP refuses first, names the affected services, and requires a matching acknowledgment before proceeding. ## Credentials +**Project-scoped token** - Zerops token that can access exactly one project. ZCP expects this scope. **`ZCP_API_KEY`** - project-scoped Zerops token used by ZCP. **`GIT_TOKEN`** - git provider credential used by remote git-push delivery when needed. **`ZEROPS_TOKEN`** - Zerops token commonly used by GitHub Actions or external CI that runs `zcli`. +**External secret** - third-party credential such as Stripe, OpenAI, Mailgun, or GitHub API access. ZCP can wire placeholders and env vars, but the secret itself remains your responsibility. ---------------------------------------- # Zcp > Overview -ZCP is the Zerops control plane setup that lets a coding agent work from real project state instead of a hand-written operations checklist. It gives the agent current project knowledge, guarded project operations, deploy/verify evidence, and Zerops guidance for connecting an app to its infrastructure. -It can run remotely inside a `zcp@1` service with **Include Coding Agent** enabled, or locally as the `zcp` binary initialized beside your CLI/editor agent. In both cases, the developer stays focused on product intent, technology choices, acceptance criteria, and decisions that require human judgment. -## What ZCP does -ZCP makes four things available to the agent inside a normal Zerops project: -**Project awareness.** The agent can read services, runtime layout, env vars, logs, events, and current work state instead of asking you to describe the project. -**Infrastructure wiring.** The agent can separate the `zcp` setup service, app runtime services, and managed dependencies, then connect the app to the right env vars and service references. -**Deploy and verify loop.** The agent can prove the app works on the selected runtime, not stop after writing code or seeing a green build. -**Delivery choice.** After verified work, the agent can keep deploying directly, push to git, or use external handoff to your CI or a human. -You provide product intent and judgment: what to build, which runtime or stage matters when you care, and what result counts as done. -## A good ZCP session -A ZCP-backed session should be product-led and evidence-based: -- The agent reads the project before changing it. -- It uses existing services when they fit and creates missing infrastructure when they do not. -- It deploys and verifies the requested behavior on the real endpoint or UI. -- It ends with a URL or proof of behavior, or with a blocker that names the missing decision, credential, or repeated failure. -## What you no longer have to do -ZCP is valuable because it removes the operational checklist from the prompt. You should not have to: -- describe every service already in the project, -- decide which env-var references connect the app to managed services, -- copy database credentials, logs, or deploy timelines into chat, -- repeat deploy, verify, log-reading, and URL-reporting instructions on every app task, -- translate a failed build or broken route into a guess before the agent can act, -- choose internal workflow names before describing what you want built. -You still own the product intent, technology constraints, acceptance criteria, credentials that live outside Zerops, and approval for destructive actions. -## Where to start -| Goal | Page | -|---|---| -| Try ZCP once with no local install | [Quickstart](/zcp/quickstart) | -| Understand the model before setup | [How ZCP works](/zcp/concept/how-it-works) | -| Choose remote vs local | [Choose remote or local setup](/zcp/setup/choose-workspace) | -| Add remote setup | [Set up remote ZCP](/zcp/setup/hosted-workspace) | -| Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | -| Build or change an app | [Build with ZCP](/zcp/workflows/build-with-zcp) | -| Decide how finished work ships | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | -| Understand tokens and boundaries | [Trust model](/zcp/security/trust-model) | -| Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | -| Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | -| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | -| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | +ZCP is a project-scoped control plane for AI coding agents over one Zerops project. It gives the agent the current truth about that project, project-scoped tools for operating it, and rules for deciding what should happen next and when the work is done. +The practical effect is that the agent can work from the project's real state instead of a guessed checklist. It can identify the right app runtime and managed services, make Zerops changes through project-scoped operations, read evidence when something fails, and finish with proof or a concrete blocker. +That changes the job you give the agent. You provide the product intent, technical constraints, and quality bar. ZCP provides the platform reality: what exists, where code runs, which dependencies are available, what was deployed, what passed verification, and what the next evidence-backed step should be. +## What ZCP gives the agent +**Project truth.** Services, app runtime layout, managed dependencies, env-var keys and references, logs, events, deploy history, verification state, and current work state. +**Zerops control.** Project-scoped operations for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. +**Workflow.** ZCP combines two work loops: infrastructure for using existing Zerops services or creating missing ones, and development for code, `zerops.yaml`, deploy, verification, and delivery choice. The goal is to keep Zerops work aligned with platform and development best practices while staying behind the product task, not becoming another checklist. +**Evidence-based completion.** A build or deploy is not the finish line. A completed app task should end with a working URL, endpoint result, UI proof, or a blocker backed by logs, events, and verification evidence. +## What you no longer have to script +Without ZCP, an app prompt often turns into an operations runbook. With ZCP, you should not need to paste: +- the service map, runtime target, dev/stage state, or managed-service inventory, +- database credentials, private hostnames, env-var references, or generated connection strings, +- build logs, runtime logs, event timelines, or a guess about why the last deploy failed, +- a deploy/verify/recovery script for every task; ZCP treats proof or a concrete blocker as part of completion, +- a recap after the chat loses context; the agent can read current ZCP status. +**You still own the decisions that need human judgment:** product intent, technology constraints, acceptance criteria, external credentials, repository policy, and approval for destructive actions. +## Where ZCP runs +The **same `zcp` binary** runs in both setups. In [remote setup](/zcp/setup/hosted-workspace), Zerops packages it inside a `zcp@1` service in the project. In [local setup](/zcp/setup/local-agent-bridge), you install the binary on your machine and connect your own editor or CLI agent to the project. The mental model is the same: one project, live state, scoped operations, deploy/verify evidence. The filesystem, network path, deploy source, and safety profile differ. +To start using ZCP, choose one path: add remote setup to a Zerops project, or initialize local setup beside your editor or CLI agent. The [Quickstart](/zcp/quickstart) uses remote setup because it needs no local install. +## Your agent, credentials, and workspace +**Agent account.** Remote setup can include a bundled agent CLI, currently Claude Code, already configured to use ZCP. Zerops wires the agent to the project; it does not provide or own your model account. You authenticate the agent with your own subscription login or API credentials. +**Zerops token.** ZCP itself connects to Zerops through a project-scoped Zerops token. In remote setup, Zerops injects that token into the `zcp@1` service. In local setup, you provide a project-scoped token when you initialize ZCP beside your editor or CLI agent. Token details live in [Tokens and credentials](/zcp/security/tokens-and-project-access). +**Workspace freedom.** The `zcp@1` service is still a normal project service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and adapt the workspace around the bundled setup. ZCP is the project control plane available to the agent; it is not a closed agent product. Details live in [Use remote workspace](/zcp/setup/hosted-workspace#make-customization-persistent). +:::caution Production boundary +Use ZCP in development or staging projects. Production should stay in a separate Zerops project and receive promoted work through your CI or release process; see [Production boundary](/zcp/security/production-policy). +::: +## Start here +First-read path: +| If you want to | Read | +| ----------------------------------------- | -------------------------------------------------------- | +| Try the guided hands-on path | [Quickstart](/zcp/quickstart) | +| Understand what happens behind the prompt | [How ZCP works](/zcp/concept/how-it-works) | +| Compare remote and local setup | [Remote or local ZCP setup](/zcp/setup/choose-workspace) | +| Start building after setup | [Build with ZCP](/zcp/workflows/build-with-zcp) | +| Start building after setup | [Build with ZCP](/zcp/workflows/build-with-zcp) | +Specific tasks and reference: +| If you want to | Read | +| ---------------------------------------- | --------------------------------------------------------------------------- | +| Use remote workspace in a project | [Use remote workspace](/zcp/setup/hosted-workspace) | +| Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | +| Decide how finished work ships | [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | +| Understand tokens and project boundaries | [Trust model](/zcp/security/trust-model) | +| Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | +| Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | +| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | +| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | ## What stays outside ZCP -ZCP is a control plane for agents, not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, scaling, and service references remain canonical for platform behavior. -## Names that matter -- **ZCP** - Zerops Control Plane in its coding-agent setup. -- **Remote setup** - a `zcp@1` service inside the Zerops project. The service runs the `zcp` binary and exposes ZCP to the agent from inside the project. -- **Local setup** - the `zcp` binary installed on your machine and initialized in a project folder with `zcp init`. -- **Include Coding Agent** - the remote setup option that adds the bundled agent CLI and preconfigures it to use ZCP in the service. -- **Cloud IDE** - optional browser-based VS Code in remote setup. -- **`zcp` binary** - the executable. In remote setup it is inside the `zcp@1` service; in local setup you install it and run `zcp init` in a project folder. -For the full vocabulary, including `zcp`, zCLI, zsc, MCP, runtime layouts, and credential names, see the [Glossary](/zcp/glossary). +ZCP is not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, scaling, and service references remain canonical for platform behavior. ---------------------------------------- # Zcp > Quickstart -Remote setup is the fastest first run: no local `zcp` install, no local MCP config, and the bundled agent is already connected to the project. You will open a project with **Include Coding Agent** enabled, give the agent a small app request, and inspect what it verified. +Use this path when you want to see ZCP working with the least setup. It is intentionally one path: deploy a recipe that already includes the **AI Agent** environment, authorize Claude Code, open Browser VS Code, and ask for product work in natural language. +Zerops has a [recipes catalog](https://app.zerops.io/recipes) for many runtimes and frameworks. Some recipes include an **AI Agent** environment, which is the ready-to-go ZCP setup: app services, managed services, the `zcp@1` workspace service, Browser VS Code, and bundled agent wiring in one deploy. +This quickstart uses the [Laravel showcase recipe](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent) because it includes real managed services and the AI Agent environment. The same flow works for other recipes that offer AI Agent. ## Prerequisites - A Zerops account with permission to create a project. -- A login for the bundled agent shown in the dashboard. -## Create a remote setup project -The fastest path is a recipe with the **AI Agent** environment: +- A Claude Code subscription login or API authentication. Claude Code is the bundled agent in the current dashboard flow; support for additional bundled agents is planned. Zerops wires Claude Code to ZCP, but your agent subscription or model credentials stay yours. +## 1. Choose the AI Agent recipe 1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes). -2. Pick a recipe that matches the stack you want to try. -3. Select the **AI Agent** environment. -4. Deploy the recipe. -The **AI Agent** environment creates the app services plus a `zcp@1` remote setup with **Include Coding Agent** enabled. Keep **Cloud IDE** enabled too so you can open the browser workspace. If you start from an empty project instead, enable **Add Zerops Control Plane (ZCP) service** during project creation and keep **Include Coding Agent** on; the agent can use existing app services or create the missing ones from your prompt. Full setup path: [Set up remote ZCP](/zcp/setup/hosted-workspace). -## Open remote setup -When provisioning finishes, open the `zcp` service in the Zerops dashboard. If **Cloud IDE** is enabled, open its workspace URL; a browser editor opens with the agent connected to this project through ZCP. -Ask for what the app should do. Service setup, deploy, verification, and reporting are part of the ZCP-backed agent contract. -In the agent chat: +2. Open a recipe with an **AI Agent** environment. For example, open [Laravel showcase](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent). +3. Select **AI Agent**. +4. Keep **Coding Agent** and **Cloud IDE** enabled. +5. Deploy the recipe. +The deploy creates the app runtimes, managed dependencies, and a workspace service using the `zcp@1` service type. In this recipe it appears as the `zcp` service. That service is where the agent, terminal, and browser IDE run. App code still deploys to the app runtime services, not to `zcp`. +## 2. Authorize Claude Code +When provisioning finishes, the dashboard opens the Claude Code authentication flow. Use your own Claude Code subscription login or API credentials. +This is separate from the ZCP project token. Zerops injects the project-scoped `ZCP_API_KEY` into the workspace service; your Claude account or API key is used only by the bundled agent. +If you close the prompt, open the project in the dashboard and then open the `zcp` service. The control-plane panel shows the browser workspace, web terminal, SSH path, desktop IDE path, and agent authorization state. +## 3. Open the workspace +After authentication, continue into **Browser VS Code**. The workspace opens with the project filesystem, ZCP configuration, terminal access, and the Claude Code panel available in the editor. +You are now inside the remote workspace. The agent can read project state, use project-scoped ZCP operations, reach project-private services, and deploy app changes to the runtime services created by the recipe. +## 4. Ask for a product outcome +In Claude Code, ask for the app behavior in natural language. A good first prompt is intentionally short: ```text -Build a task board where tasks stay saved after refresh. +Build a task board for my team. +Tasks should stay saved after refresh. ``` -Add constraints only when they change the product, runtime layout, or delivery path: +The agent is expected to deploy, verify, read evidence, and return proof for the app task. Reserve extra prompt detail for decisions that change the work. +Add details when they change the product, stack, runtime layout, acceptance criteria, or delivery path: ```text -Build a task board where tasks stay saved after refresh. Use the existing dev+stage pair if this project has one. +In this Laravel app, add a task board backed by the existing PostgreSQL service. +A user can create a task, refresh the page, and still see it. ``` -```text -After it works, set up git-push delivery to git@github.com:my-org/task-dashboard.git. -``` -## What you should see -A good run should make these points clear: -1. The agent read current project state before changing anything. -2. It named the app runtime it will change. The `zcp` service is the ZCP setup, not the app target. -3. It used existing services when they already fit, or created missing services before app work. -4. It changed code and `zerops.yaml` as needed. -5. It deployed through Zerops and read logs or events if something failed. -6. It verified both platform reachability and the requested dashboard behavior. -7. It ended with a URL or a concrete blocker. -Open the URL the agent gives you. If the page loads but the requested behavior is missing, the task is not done; ask the agent to verify that exact behavior again. +:::note Prompt shape +Short prompts work when they describe the product outcome. Add stack, runtime layout, acceptance criteria, delivery preference, external credentials, or destructive-action approval only when those decisions matter. ZCP carries project discovery, Zerops wiring, deploy, verification, evidence reading, and recovery behind that request. +::: +## 5. Read the proof +The final answer should give you a real URL, endpoint, UI state, stored result, or a concrete blocker. Open the URL and try the core behavior the agent says it verified. +For the task-board prompt, create a task, refresh the page, and confirm the task is still there. If it disappears, the app is not done; ask the agent to verify that exact behavior again and continue from the project evidence rather than starting over. +If the agent cannot finish, the useful output is a concrete blocker: the missing credential, decision, unsupported runtime choice, or repeated failure it could not recover from. ## Next steps -- [How ZCP works](/zcp/concept/how-it-works) - the model behind the run. -- [Build with ZCP](/zcp/workflows/build-with-zcp) - how real app work flows. -- [Choose how finished work ships](/zcp/workflows/delivery-handoff) - direct deploy, git-push, or external handoff. -## Gotchas -- **Deploy success is not done.** The app behavior you requested must be verified on the real URL or endpoint. -- **The `zcp` service is not the app.** Runtime services such as `appdev`, `appstage`, or `app` receive app code and deploys. -- **Production stays out of the agent loop.** Use ZCP in dev/staging projects; promote to production through your CI or release process. +Read [How ZCP works](/zcp/concept/how-it-works) if you want the model behind what just happened: project state, runtime fit, app wiring, deploy evidence, behavior proof, and blockers. +Read [Remote or local ZCP setup](/zcp/setup/choose-workspace) when you are deciding where the agent workspace should live for a real project. Remote setup keeps the agent inside Zerops; local setup keeps the agent next to your local files, editor, data, and tools. +Read [Build with ZCP](/zcp/workflows/build-with-zcp) before giving the agent larger tasks. It explains the practical choices you can control during normal work: runtime layout, development context, and delivery preference. +Before promoting anything to production, read [Production boundary](/zcp/security/production-policy). ZCP belongs in development or staging projects; production should stay in a separate Zerops project and receive verified work through your release path. ---------------------------------------- # Zcp > Reference > Agent Workflow -Exact vocabulary for ZCP workflows: runtime layout, verification, delivery, failure state, and completion evidence. Day-to-day app prompts should use outcomes; policies and handoffs can use these names when precision matters. +Exact vocabulary for ZCP workflows. Day-to-day prompts should describe outcomes; reference, audits, policies, and handoffs can use these names when precision matters. ## Session layers -| Layer | What it is | What changes here | -|---|---|---| -| ZCP setup | Where ZCP runs: remote `zcp@1` service or local `zcp` binary. | No app code should be deployed to the setup itself. | -| Target runtime service | The app runtime: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys. | -| Managed services | Databases, caches, queues, search, storage. | Schema/data operations, never app code deploys. | -| Managed services | Databases, caches, queues, search, storage. | Schema/data operations, never app code deploys. | +Most workflow mistakes come from confusing these layers: +| Layer | What it is | What changes here | +| ---------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------- | +| ZCP setup | Where ZCP runs: remote `zcp@1` service or local `zcp` binary. | No app code should be deployed to the setup itself. | +| Target runtime service | The app runtime: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys. | +| Managed services | Databases, caches, queues, search, storage, mail. | Schema/data operations and credentials, never app code deploys. | +| Managed services | Databases, caches, queues, search, storage, mail. | Schema/data operations and credentials, never app code deploys. | The `zcp` service is the control surface, not the app runtime. -## Service setup -Before code work starts, ZCP should identify the app runtime services and managed dependencies. It should use existing services when they fit, create missing services when the project is empty, and resume from live project state if setup was interrupted. -The service setup phase stops before app edits begin; the full ZCP work session then continues into code, deploy, verification, and recovery. -## App work completion contract -For an app intent, ZCP expects the agent to handle code, config, deploy, verify, and retries behind the prompt. A completed session leaves evidence of: -- runtime scope, -- app code and `zerops.yaml` changes, -- a direct deploy for the first verified runtime, -- platform reachability, -- requested behavior on the real endpoint or UI, -- evidence-based retries when something failed, -- a URL or blocker. -## Verification layers -| Layer | What it proves | -|---|---| -| Runtime reachability | Service status, recent error logs, and HTTP probe for eligible runtime services. | -| Requested behavior | The user-requested endpoint, UI state, job result, database state, or stage URL works. | -| Requested behavior | The user-requested endpoint, UI state, job result, database state, or stage URL works. | -Both layers must pass before a session reports completion. +## Service setup routes +Before app code work starts, ZCP reads project state and chooses a setup route. The route is an implementation detail in first-read docs, but it is useful in reference and audits. +```mermaid +flowchart TD + start(["Read project state"]) + mid{"Interrupted setup +to resume?"} + runtimes{"Runtime services +already exist?"} + known{"Request matches +known recipe/stack?"} + resume(["resume"]) + adopt(["adopt"]) + recipe(["recipe"]) + classic(["classic"]) + start --> mid + mid -- yes --> resume + mid -- no --> runtimes + runtimes -- yes --> adopt + runtimes -- no --> known + known -- yes --> recipe + known -- no --> classic +``` +| Route | Use when | Wrong signal | +| --------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | +| `adopt` | Runtime services already exist, including recipe-created projects. | Recreating services that exist, or targeting `zcp` as the app. | +| `recipe` | The project is empty or only has ZCP, and the request matches a known stack recipe. | Deploying an unchanged starter as if it were the requested product. | +| `classic` | The project is empty and needs a custom service plan. | Writing app code before service ownership and runtime scope are known. | +| `resume` | A previous setup was interrupted. | Starting from scratch without acknowledging live services and saved state. | +| `resume` | A previous setup was interrupted. | Starting from scratch without acknowledging live services and saved state. | +Service setup stops when app runtimes and managed dependencies are known. App code, `zerops.yaml`, and the first deploy belong to the develop flow. +## Develop flow +The develop loop is the main ZCP work cycle. It closes only when reachability and requested behavior both pass. +```mermaid +flowchart TD + scope["1. Name runtime scope"] + change["2. Change code/config"] + deploy["3. Deploy in-scope runtime"] + reach{"4. Runtime reachability +passes?"} + behavior{"5. Requested behavior +passes?"} + done(["Done: proof or URL"]) + fix["6. Categorize failure, +read evidence, fix"] + blocker(["Blocker"]) + scope --> change --> deploy --> reach + reach -- yes --> behavior + reach -- no --> fix + behavior -- yes --> done + behavior -- no --> fix + fix --> deploy + fix --> blocker +``` +1. **Name runtime scope.** State which runtime is in scope (`appdev`, `appstage`, `app`, or linked local target). In dev+stage projects, dev work does not imply stage unless requested. +2. **Change code/config.** Edit app files, `zerops.yaml`, env references, migrations, seeds, or framework config. +3. **Deploy directly first.** The first verified runtime deploy uses the direct ZCP deploy path. Delivery setup is applied after a verified running result exists. +4. **Verify runtime reachability.** Service status, recent error logs, and HTTP probe for eligible runtime services. +5. **Verify requested behavior.** Endpoint body, UI state, job result, persisted data, or another check tied to the user request. +6. **Fix from evidence.** Read classification, logs, events, and check output. Repeating the same deploy without new evidence is not progress. +Dynamic dev runtimes may need an explicit start or restart after deploy. Built-in webserver runtimes do not need a separate dev-server step unless the framework requires one. ## Runtime layouts -| Layout | Meaning | -|---|---| -| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage must be named when included. | -| `dev` | One mutable development runtime. | -| `simple` | One runtime with no dev/stage split. | -| `local-stage` | Local checkout linked to one Zerops runtime for deploys. | -| `local-only` | Local checkout with no linked deploy target yet. | -| `local-only` | Local checkout with no linked deploy target yet. | Runtime layout describes which app runtime services ZCP should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. -## Delivery choice -Delivery choice applies after a verified deploy and describes future changes. -| Choice | Meaning | -|---|---| -| Direct deploy | ZCP keeps deploying changes directly to the target runtime. | -| Git push | The agent commits and pushes to a configured remote; any resulting build still needs verification. | -| External handoff | Your CI, release process, or a human owns future delivery. | -| External handoff | Your CI, release process, or a human owns future delivery. | +| Layout | Meaning | Typical names | +| ------------- | ---------------------------------------------------------------------------------------------- | ---------------------------- | +| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage is explicit. | `appdev` + `appstage` | +| `dev` | One mutable development runtime. | `appdev` | +| `simple` | One runtime with no dev/stage split. | `app` | +| `local-stage` | Local source directory linked to one Zerops runtime for deploys. | local directory + `appstage` | +| `local-only` | Local source directory with no linked deploy target yet. | local directory | +| `local-only` | Local source directory with no linked deploy target yet. | local directory | +The stage hostname is supplied by project state. ZCP should not invent it from the dev hostname. +## Delivery modes +Delivery mode applies after a verified deploy and describes how future changes ship. It does not redirect the first successful deploy. +| Exact mode | User-facing choice | Meaning | +| ---------- | ------------------ | ------------------------------------------------------------------------------------------------------------------ | +| `auto` | Keep direct deploy | ZCP keeps deploying future changes directly to the target runtime. | +| `git-push` | Push to git | The agent commits and pushes to a configured remote; any resulting build still needs observation and verification. | +| `manual` | External handoff | CI, release process, or a human owns future delivery; ZCP records evidence but does not initiate the next deploy. | +| `manual` | External handoff | CI, release process, or a human owns future delivery; ZCP records evidence but does not initiate the next deploy. | +Git-push capability, delivery mode, and build integration are separate. A project can have git-push configured while still using direct deploy for a given session. ## Failure categories -| Category | Meaning | Read first | -|---|---|---| -| `build` | Build phase failed. | Build logs and build commands. | -| `start` | Build passed, runtime failed to start or crashed. | Runtime/prepare logs and start command. | -| `verify` | Runtime exists but reachability or behavior failed. | Failing check detail and runtime logs at request time. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and network path. | -| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Structured rejection detail and config files. | -| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | -| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | -| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +| Category | Meaning | Read first | +| ------------ | --------------------------------------------------------------------- | ------------------------------------------------------ | +| `build` | Build phase failed. | Build logs and build commands. | +| `start` | Build passed, runtime failed to start or crashed. | Prepare/runtime logs and start command. | +| `verify` | Runtime exists but reachability or behavior failed. | Failing check detail and runtime logs at request time. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and network path. | +| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Structured rejection detail and config files. | +| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | +| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +Categorization is what turns retries into evidence-driven fixes. Recovery moves and symptom mapping live in [Troubleshooting](/zcp/reference/troubleshooting). ## Completion evidence -A well-run session can answer: +A completed app task can answer: - which services were used or created, -- which runtime scope was changed, +- which runtime scope changed, - which deploy passed, -- which reachability and behavior checks passed, -- which URL or endpoint proves the result, -- which delivery contract applies next, +- which reachability check passed, +- which user-requested behavior passed, +- which URL, endpoint, UI state, or stored result proves it, +- which delivery mode applies next, - or which blocker remains and what evidence supports it. +A green build with a broken route is not completion. A clear blocker is acceptable completion only when it names the failure category, evidence read, fixes tried, and human decision or credential still needed. +## Practical rules +- Use existing services when they fit; do not recreate them just to get a clean slate. +- Bootstrap/service setup is not app work; app files and first deploy happen in develop. +- First functional deploy is direct; git/CI/handoff comes after proof. +- Managed services are dependencies, not deploy targets. +- Stage is explicit in `standard` projects; dev changes do not silently promote. +- Runtime health and requested behavior are separate verification gates. +- Stop on repeated failure without new evidence, missing credentials, ambiguous runtime scope, or destructive recovery. +## Auditing a session +A session is well-shaped if platform evidence and the agent report answer: +| Question | Evidence | +| ----------------------------------------------- | ------------------------------------------------------------------------- | +| Which setup route and runtime layout were used? | ZCP status, service metadata, project services. | +| Where did service setup end and app work begin? | Work-session state, deploy history, file changes. | +| Which runtime was deployed and verified? | Service events, deploy result, verify output. | +| What behavior proved success? | Endpoint/UI/job/data proof in the final report. | +| What controls future delivery? | Delivery mode, git-push state, build integration, or manual handoff note. | +| What controls future delivery? | Delivery mode, git-push state, build integration, or manual handoff note. | Operation-name reference lives in [Advanced operations](/zcp/reference/mcp-operations). Recovery shortcuts live in [Troubleshooting](/zcp/reference/troubleshooting). ---------------------------------------- @@ -26119,7 +26330,7 @@ Operation-name reference lives in [Advanced operations](/zcp/reference/mcp-opera Exact terms, operation names, and recovery detail live here. Day-to-day app work starts in [Build with ZCP](/zcp/workflows/build-with-zcp). | Need | Page | |---|---| -| Name the workflow phases, runtime layouts, verification layers, and delivery choices | [Workflow terms](/zcp/reference/agent-workflow) | +| Name service setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence | [Workflow terms](/zcp/reference/agent-workflow) | | Look up common MCP operation names for agent-client policy or debugging | [Advanced operations](/zcp/reference/mcp-operations) | | Turn a running service into a re-importable bundle | [Package a running service](/zcp/workflows/package-running-service) | | Recover a stuck or confused session | [Troubleshooting](/zcp/reference/troubleshooting) | @@ -26132,55 +26343,55 @@ Exact terms, operation names, and recovery detail live here. Day-to-day app work Most users never need operation names. They matter when you configure agent-client policy, debug an MCP integration, or automate ZCP directly. In normal app work, ask the agent for outcomes. Recipe-authoring and ZCP-internal maintenance operations are intentionally out of scope. ## Permission classes -| Class | Meaning | -|---|---| -| Read-only | Inspect state, logs, events, guidance, or verification output. | -| Mutating | Change services, env vars, scaling, deploy state, or lifecycle. | +| Class | Meaning | +| ----------------- | ----------------------------------------------------------------------------------------- | +| Read-only | Inspect state, logs, events, guidance, or verification output. | +| Mutating | Change services, env vars, scaling, deploy state, or lifecycle. | | Operational setup | Coordinate workflows, mounts, imports, recipes, delivery setup, or generated local files. | | Operational setup | Coordinate workflows, mounts, imports, recipes, delivery setup, or generated local files. | ## Read-only operations -| Operation | Purpose | -|---|---| -| `zerops_discover` | Read services, ports, env-var keys, and current state. | -| `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | -| `zerops_events` | Read service-scoped project activity, deploys, builds, scaling, and failures. | -| `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | -| `zerops_knowledge` | Fetch ZCP guidance or platform knowledge for the current state. | -| `zerops_process` | Check a known async process; cancel is a mutating action. | -| `zerops_process` | Check a known async process; cancel is a mutating action. | +| Operation | Purpose | +| ------------------ | ----------------------------------------------------------------------------- | +| `zerops_discover` | Read services, ports, env-var keys, and current state. | +| `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | +| `zerops_events` | Read service-scoped project activity, deploys, builds, scaling, and failures. | +| `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | +| `zerops_knowledge` | Fetch ZCP guidance or platform knowledge for the current state. | +| `zerops_process` | Check a known async process; cancel is a mutating action. | +| `zerops_process` | Check a known async process; cancel is a mutating action. | ## Mutating operations -| Operation | Purpose | -|---|---| -| `zerops_deploy` | Ship code through the Zerops build and deploy pipeline. | -| `zerops_env` | Read, set, delete, or generate env vars and local `.env` files. | -| `zerops_manage` | Start, stop, restart, reload, or connect storage. | -| `zerops_scale` | Change CPU, RAM, disk, CPU mode, or container autoscaling where supported. `HA`/`NON_HA` is set at service creation. | -| `zerops_subdomain` | Enable or disable public subdomain access. | -| `zerops_delete` | Delete a service. Explicit named approval is required. | -| `zerops_delete` | Delete a service. Explicit named approval is required. | +| Operation | Purpose | +| ------------------ | -------------------------------------------------------------------------------------------------------------------- | +| `zerops_deploy` | Ship code through the Zerops build and deploy pipeline. | +| `zerops_env` | Read, set, delete, or generate env vars and local `.env` files. | +| `zerops_manage` | Start, stop, restart, reload, or connect storage. | +| `zerops_scale` | Change CPU, RAM, disk, CPU mode, or container autoscaling where supported. `HA`/`NON_HA` is set at service creation. | +| `zerops_subdomain` | Enable or disable public subdomain access. | +| `zerops_delete` | Delete a service. Explicit named approval is required. | +| `zerops_delete` | Delete a service. Explicit named approval is required. | ## Operational setup -| Operation | Purpose | -|---|---| -| `zerops_workflow` | Track, recover, or configure ZCP work sessions; also carries the package-running-service export flow. | -| `zerops_import` | Import project/service definitions. Destructive override is gated. | -| `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | -| `zerops_preprocess` | Expand Zerops preprocessor expressions. | -| `zerops_export` | Read platform project/service export YAML and service metadata. | -| `zerops_export` | Read platform project/service export YAML and service metadata. | +| Operation | Purpose | +| ------------------- | ----------------------------------------------------------------------------------------------------- | +| `zerops_workflow` | Track, recover, or configure ZCP work sessions; also carries the package-running-service export flow. | +| `zerops_import` | Import project/service definitions. Destructive override is gated. | +| `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | +| `zerops_preprocess` | Expand Zerops preprocessor expressions. | +| `zerops_export` | Read platform project/service export YAML and service metadata. | +| `zerops_export` | Read platform project/service export YAML and service metadata. | ## Remote and local differences -| Area | Remote setup | Local setup | -|---|---|---| -| Files | Runtime files through SSHFS/project containers | Local working directory | -| Deploy source | Project service or batch deploy inside Zerops | Local `workingDir` | -| Dev server | ZCP can run/inspect project-container dev processes | Your local tool owns the dev server | -| Env bridge | Project env is available in the project | `.env` generation resolves project references for local app use | -| Git | Workspace-managed credentials | User's local git credentials | -| Git | Workspace-managed credentials | User's local git credentials | +| Area | Remote setup | Local setup | +| ------------- | --------------------------------------------------- | --------------------------------------------------------------- | +| Files | Runtime files through SSHFS/project containers | Local working directory | +| Deploy source | Project service or batch deploy inside Zerops | Local `workingDir` | +| Dev server | ZCP can run/inspect project-container dev processes | Your local tool owns the dev server | +| Env bridge | Project env is available in the project | `.env` generation resolves project references for local app use | +| Git | Workspace-managed credentials | User's local git credentials | +| Git | Workspace-managed credentials | User's local git credentials | ## Confirmation gates Two operations require explicit care: - **Service deletion** requires explicit user approval in the current conversation, by service name. - **Destructive import override** first refuses and names what would be replaced; a second call must acknowledge the same targets. -See [Tokens and credentials](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions) for the user-facing confirmation flow. +See [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) for the user-facing confirmation flow. ---------------------------------------- @@ -26190,35 +26401,87 @@ Start recovery from current project state, not from chat memory. ```text Read ZCP status and tell me where this project stands before changing anything. ``` -That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. -## Failure categories -| Category | Meaning | First move | -|---|---|---| -| `build` | Build failed before a runtime could start. | Read build logs and `zerops.yaml` build steps. | -| `start` | Build passed, runtime failed to start or crashed. | Read runtime/prepare logs and check start command, ports, and env vars. | -| `verify` | Runtime exists, but reachability or requested behavior failed. | Read the failing check, HTTP response, and runtime logs at request time. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Check the named network path and, for local setup, VPN. | -| `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Read the structured rejection and fix the named field. | -| `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | Fix the named credential; do not rotate unrelated tokens. | -| `other` | No known category matched. | Read raw events/logs and stop if the same unknown failure repeats. | -| `other` | No known category matched. | Read raw events/logs and stop if the same unknown failure repeats. | -## Common symptoms -| Symptom | Likely move | -|---|---| -| Build log shows missing command/module/lock file | Fix build commands, manifests, deploy files, or dependencies. | -| Runtime is `READY_TO_DEPLOY` or restarts continuously | Read runtime logs; the start command or env contract is usually wrong. | -| Public URL returns 502 or connection error | Check port binding, `0.0.0.0`, subdomain readiness, and whether the service is HTTP-eligible. | -| Deploy succeeded but feature is broken | Treat it as behavior verification failure, not deploy success. | -| Local app cannot reach `db`, `redis`, or storage | Bring VPN up and regenerate `.env` if project env changed. | -| Agent targets `zcp` as the app | Correct it to the runtime service. | -| Agent loops on the same failure | Ask for category, evidence read, fixes attempted, and the next human decision. | -| Agent loops on the same failure | Ask for category, evidence read, fixes attempted, and the next human decision. | +That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. ZCP can rebuild the project picture from live services, recent deploys, logs, events, and saved work-session state. +## Start from the failure category +When a deploy or verify step fails, ZCP should surface a category with the likely cause and next move. Categories are recovery-shaped. `network` does not mean "the network is broken"; it means the first useful move is connectivity, VPN, SSH, DNS, or public-route evidence rather than editing app code. +| Category | Meaning | Read first | Recovery move | +| ------------ | --------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `build` | Build failed before runtime start. | Build logs and `zerops.yaml` build steps. | Fix build commands, dependency manifests, lock files, deploy files, or build resources. Runtime logs will not explain this failure. | +| `start` | Build passed, runtime failed to start or crashed. | Prepare logs or runtime logs, whichever the failure names. | Check `run.start`, ports, env references, `prepareCommands`, and whether the process binds `0.0.0.0` rather than `127.0.0.1`. | +| `verify` | Runtime exists, but reachability or requested behavior failed. | The failing check, HTTP response, and runtime logs at request time. | Treat it as app behavior or route recovery, not deploy success. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | The transport error and the named network path. | Check VPN for local setup, service status for remote setup, SSH transfer state, DNS/subdomain readiness, or platform timeout. | +| `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Structured rejection detail, field path, or API metadata. | Fix the named setup block, env reference, `deployFiles`, service type, or prerequisite. | +| `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | The credential surface named in the error. | Replace that credential only: `ZCP_API_KEY`, `GIT_TOKEN`, local git auth, `ZEROPS_TOKEN`, SSH key, or app secret. | +| `other` | No known category matched. | Raw reason, service events, and logs for the named phase. | Stop if the same unknown reason repeats; report the raw reason and recent evidence. | +| `other` | No known category matched. | Raw reason, service events, and logs for the named phase. | Stop if the same unknown reason repeats; report the raw reason and recent evidence. | +Build logs and runtime logs are different streams. A build failure shows in the build container; a runtime crash shows in runtime logs. If the agent reads the wrong stream, the diagnosis will be wrong even when the log command succeeded. +## Deploy and start symptoms +| Symptom | Likely category | Next move | +| --------------------------------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------- | +| Build exits with "command not found", missing module, npm 404, or compile error | `build` | Read `buildCommands`, manifests, lock files, package manager, and deploy file list. | +| Build or SSH transfer ends with `signal: killed` | `build` / `network` | Treat it as memory pressure first: scale build/source resources or reduce the heavy step. | +| Runtime crashes immediately with `EADDRINUSE`, missing module, or missing env var | `start` | Read runtime logs scoped to the failed start and check the start command/env contract. | +| Database connection refused during init/start | `start` / `network` | Confirm the managed service is running and env references such as `${db_*}` resolve. | +| Preflight refuses `INVALID_ZEROPS_YML` or setup mismatch | `config` | Read the field-level rejection and fix that setup entry; setup names are not always hostnames. | +| Git push returns authentication failed | `credential` | Remote setup needs `GIT_TOKEN`; local setup uses your local git credentials. | +| Git push returns authentication failed | `credential` | Remote setup needs `GIT_TOKEN`; local setup uses your local git credentials. | +Useful prompt: +```text +Show me the failure category, build logs, runtime logs, and recent events for the last failed deploy of appdev. +``` +## Verify and public URL symptoms +A deploy can be green and still fail verification. Verify has two layers: platform reachability, then the requested behavior. +| Symptom | Likely cause | Next move | +| ------------------------------------------------------------ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| Service is `RUNNING`, but the route returns 500 or 404 | App route, dependency, or request handling is broken. | Read runtime logs at request time and verify the requested route/body, not only `/`. | +| Public URL returns connection error right after first deploy | Subdomain route is still propagating, or the runtime is not bound correctly. | Wait 30-60 seconds and retry; if it persists, check port binding and public-access eligibility. | +| 502 persists after deploy and wait | Start command binds `127.0.0.1`, wrong port, or HTTP server not listening. | Check `zerops.yaml` `run.ports[]` and make the app bind the same port on `0.0.0.0`. | +| Subdomain stays disabled | Worker or non-HTTP service, or explicit public access missing. | Workers do not get useful public URLs. If it is an HTTP runtime, ask the agent to enable subdomain access and verify it. | +| Requested feature fails after a successful deploy | Behavior verification failed. | Continue the loop from logs/events/check output; do not report the task done. | +| Requested feature fails after a successful deploy | Behavior verification failed. | Continue the loop from logs/events/check output; do not report the task done. | Public subdomain access is enabled on first deploy for eligible HTTP runtimes. Workers and non-HTTP services do not get a useful public URL by design. +## Token and credential symptoms +Token problems usually happen at startup. The fix is to replace the token on the surface ZCP reads, then restart the agent so the new process inherits it. +| Symptom | Likely cause | Next move | +| -------------------------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `Token accesses N projects; use project-scoped token` | Multi-project or account-wide token. | Generate a token scoped to exactly one project and replace `ZCP_API_KEY`. | +| `Token has no project access` | Token authenticates but reaches no project. | Grant project access or generate a new project-scoped token. | +| `No authentication found: set ZCP_API_KEY or log in with zcli` | No token reached ZCP. | Add `ZCP_API_KEY` under `.mcp.json` `env` for local setup, or check remote service env. | +| API replies with 401 or `AUTH_TOKEN_EXPIRED` | Token expired or was revoked. | Generate a new project-scoped token, replace it, restart the agent. | +| GitHub Actions deploy fails on auth | `ZEROPS_TOKEN` missing/wrong. | Set a Zerops API token as the repository secret named `ZEROPS_TOKEN`. It is not a GitHub token. | +| GitHub Actions deploy fails on auth | `ZEROPS_TOKEN` missing/wrong. | Set a Zerops API token as the repository secret named `ZEROPS_TOKEN`. It is not a GitHub token. | +Full credential model: [Tokens and credentials](/zcp/security/tokens-and-project-access). +## Local setup symptoms +These apply to local setup. Remote setup is already on the project-private network and does not need VPN from your laptop. +| Symptom | Likely cause | Next move | +| --------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | +| Local app cannot reach `db`, `redis`, `cache`, or storage hostname | VPN is down or dropped after sleep/network change. | Run `zcli vpn up ` again. | +| `no route to host` or connection refused for managed-service hostname | Same network path issue. | Bring VPN up, then retry. If network works, regenerate `.env`. | +| Local app reads stale project credentials | `.env` is a snapshot. | Ask ZCP to regenerate the `.env` after project env changes. | +| Re-running `zcp init` made ZCP disappear from the agent | `.mcp.json` was rewritten without `ZCP_API_KEY`. | Re-add the `env` block and restart the agent from the project root. | +| Agent does not list the `zerops` MCP server | Agent launched from the wrong directory or config not loaded. | Quit and relaunch from the directory containing `.mcp.json`. | +| `zcp` is not found after install | Install path is not on `PATH`. | Add `~/.local/bin` or the install target to your shell `PATH`, then restart the shell/agent. | +| `zcp` is not found after install | Install path is not on `PATH`. | Add `~/.local/bin` or the install target to your shell `PATH`, then restart the shell/agent. | +## Session drift and manual takeover +Use status when the chat-side picture drifts from reality: +```text +Read ZCP status, list the services in scope, and summarize the last deploy and verify result before changing anything. +``` +That is the right move when the agent says it has no context, a browser workspace was reopened, a CLI session restarted, or you are taking over from a previous run. +For manual takeover, read evidence in this order: +| Evidence | Why it matters | +| --------------------------- | ------------------------------------------------------------------------------------------------- | +| Service-scoped events | Shows deploys, build/start transitions, failed process reasons, scaling, lifecycle actions. | +| Build logs and runtime logs | Separates build failures from runtime crashes and request-time app errors. | +| Verify output | Shows whether reachability and requested behavior actually passed. | +| Git history | Matters when delivery uses git-push; shows what source change corresponds to the deployed result. | +| Git history | Matters when delivery uses git-push; shows what source change corresponds to the deployed result. | ## When to stop Stop the loop when the agent cannot make progress with new evidence, needs a missing credential, is about to delete or replace a service, or the target runtime/stage is ambiguous. +A repeated retry with the same evidence is not recovery. Ask the agent for the category, evidence read, fixes attempted, and the next human decision. Before destructive recovery, read service-scoped events, logs, deploy/verify result, and git history when delivery uses git-push. ## Related -- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) +- [Build with ZCP](/zcp/workflows/build-with-zcp) - [Workflow terms](/zcp/reference/agent-workflow) - [Tokens and credentials](/zcp/security/tokens-and-project-access) - [Set up local ZCP](/zcp/setup/local-agent-bridge) @@ -26227,77 +26490,123 @@ Before destructive recovery, read service-scoped events, logs, deploy/verify res # Zcp > Security > Production Policy -Rule: keep ZCP in a development or staging Zerops project. Production should be a separate project without a `zcp` service; promotion happens through CI or a release pipeline with production-scoped credentials. -The platform doesn't stop you from adding a `zcp` service to a production project. Treat the dev/staging boundary as load-bearing because crossing it puts an agent inside production, not because Zerops will refuse the configuration. -## Why a separate production project -The clean separation is the project boundary: ZCP runs where the agent works, while production runs in its own 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 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 with no `zcp` service, 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 architecture pattern production uses — same runtime version, same managed service types, same build/deploy path — before promotion to the separate production project. -Use stage as the production-like rehearsal: -- The agent develops on dev (`appdev` in the `standard` runtime layout). -- 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-like failures show up. +Rule: use ZCP in development or staging projects. Production should be a separate Zerops project without a `zcp` service, and production deploys should come from CI, a release pipeline, or a deliberate human `zcli` push using production-scoped credentials. +Zerops does not prevent you from adding a `zcp` service to production. The policy exists because ZCP gives a coding agent project-scoped operational access. In production, that is the wrong blast radius for normal app development. +## Recommended project layout +| Project | Contains | Who operates it | +| ----------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------ | +| Development / staging project | `zcp` service, agent workspace, dev runtime, optional stage runtime, non-production managed services | Agent plus humans | +| Production project | Production runtimes, production managed services, production env values, production backups and scaling | CI/release process plus humans | +| Production project | Production runtimes, production managed services, production env values, production backups and scaling | CI/release process plus humans | +The two projects use different Zerops tokens. A token scoped to the development project cannot reach production. A production token should not be placed in the development `zcp` service or a local `.mcp.json` used by the agent. +## Why production is a separate project +- **The project is the security boundary.** ZCP binds to one project at startup. Keeping production in another project prevents a development agent from touching production by accident. +- **Secrets stay clean.** Production env values, database credentials, object-storage keys, and third-party secrets stay in the production project. +- **Operational policies can differ.** Production often needs HA services, stronger backup retention, stricter scaling, alerts, and release approvals. Development can stay cheaper and more flexible. +- **Audit trails stay readable.** Development experiments and agent retries do not mix with production deploy evidence. +This separation matters even when the same source repository deploys to both projects. +## What stage proves +Stage is the production-like rehearsal inside the development/staging project. It is where the agent proves the change before promotion leaves the ZCP project. +Use stage to match production where it matters: +- same runtime family and version, +- same managed service types, +- same build and start command pattern, +- same deploy path, +- behavior checks against a real running service. +Stage is not production. It should use non-production data and non-production secrets. A green stage means the change is ready for the production release path, not that the agent should deploy to production itself. ## 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. +After stage verifies, ZCP's job is done for that change. Promotion to production happens outside the agent loop: +- CI deploys a merged commit or release tag to the production project. +- A release pipeline runs `zcli push` with a production-scoped `ZEROPS_TOKEN`. +- A human runs `zcli push` against the production project with production credentials. +The agent can prepare the handoff by pushing code, summarizing verification evidence, and naming the runtime and URL it verified. It should not bridge the development project to the production project. 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 operational access to 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-like 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 ZCP setup | -| Run the agent's deploys directly into production from a development session | Production deploys come from the production project's pipeline, not the development ZCP setup | +## Credential rules +| Credential | Production policy | +| ----------------------------------- | -------------------------------------------------------------------------------------------------------- | +| Development `ZCP_API_KEY` | Never grants production access. Keep it scoped to the development/staging project. | +| Production `ZEROPS_TOKEN` | Store only in the production CI/release secret store. Do not place it in the development `zcp` service. | +| Agent subscription or model API key | May be used by the agent, but it does not grant Zerops production access by itself. | +| Git credentials | May push source changes, but production deploy authority should still be controlled by the release path. | +| Git credentials | May push source changes, but production deploy authority should still be controlled by the release path. | +If a production deploy fails, investigate in the production project with production logs, events, backups, and CI output. Do not attach the development agent directly to production as a shortcut. +## Production separation rules +| Rule | Why it matters | +| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------- | +| Keep the production project without a `zcp` service | Production should not host an agent workspace with project-scoped operational access. | +| Keep production tokens out of local `.mcp.json` | Local agents should operate development or staging projects, not production. | +| Keep production `ZEROPS_TOKEN` in the release secret store | Development tooling should not deploy to production outside the release path. | +| Treat stage proof as release evidence, not production approval | Stage is the rehearsal; production approval is a separate release decision. | +| Promote through CI, release tooling, or a deliberate human action | Production execution should use the production credential boundary. | +| Promote through CI, release tooling, or a deliberate human action | Production execution should use the production credential boundary. | +## Acceptable agent involvement +The agent can still help before production promotion: +- make the code change in development, +- deploy and verify dev/stage runtimes, +- produce the URL, endpoint result, or UI proof it verified, +- push or prepare the code path your team uses for review, +- summarize release notes and known blockers for the human or CI handoff. +ZCP's involvement stops at the handoff. Production execution belongs to the production release path. ## 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. -- [Troubleshooting](/zcp/reference/troubleshooting) — recover or take over before promoting. -- [Backup](/features/backup) — the production data safety net that operates outside ZCP. -- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) — the dev/stage loop this policy assumes. +- [Trust model](/zcp/security/trust-model) - the project boundary that makes this policy enforceable. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - project-scoped token handling. +- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare development setup paths. +- [Build with ZCP](/zcp/workflows/build-with-zcp) - the dev/stage loop this policy assumes. +- [Backup](/features/backup) - production recovery outside ZCP. ---------------------------------------- # Zcp > Security > Tokens And Project Access -ZCP enforces the project boundary at three layers: token shape (one project, exactly), where the token lives (platform-injected for remote setup, in `.mcp.json` for local setup), and confirmation gates on operations that are not reversible from inside the conversation. -## Recommended token shape — one project, full access -ZCP requires a Zerops API token that resolves to **exactly one project** at startup. For normal agent workflows, use **Custom access per project** scoped to a single project with **Full access**. Read-only tokens can pass startup, but they fail as soon as the agent tries to deploy, write env vars, or operate services. -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. +ZCP uses a Zerops API token to operate exactly one project. The important rule is simple: `ZCP_API_KEY` is a project credential for ZCP, not an agent login, not a git token, and not a general account token. +Remote setup gets `ZCP_API_KEY` from the Zerops platform. Local setup reads it from `.mcp.json`. In both paths, ZCP validates the token at startup and refuses tokens that resolve to no project or multiple projects. +## Credential map +| Name | What it authorizes | Where it belongs | +| ---------------------------- | -------------------------------------------- | --------------------------------------------------------------------------- | +| `ZCP_API_KEY` | ZCP against one Zerops project | Remote: `zcp` service env injected by Zerops. Local: `.mcp.json` env block. | +| Agent login or model API key | The coding agent itself | The bundled agent in remote setup, or your local agent client. | +| `GIT_TOKEN` | Git push from remote setup to a git provider | Secret env var on the `zcp` service when remote git-push delivery needs it. | +| `ZEROPS_TOKEN` | `zcli` in GitHub Actions or external CI | Separate project-scoped Zerops token stored in the CI/release secret store. | +| `ZEROPS_TOKEN` | `zcli` in GitHub Actions or external CI | Separate project-scoped Zerops token stored in the CI/release secret store. | +Keeping these names separate prevents most setup and delivery failures. +## Recommended `ZCP_API_KEY` shape +Use a Zerops API token with **Custom access per project**, scoped to exactly one project, with **Full access** for normal agent work. +Read-only tokens can authenticate, but they fail as soon as the agent needs to deploy, write env vars, restart services, scale, or change public access. Account-wide or multi-project tokens are refused before the agent can operate. +To generate the token: +1. Open [Settings -> Access Tokens Management](https://app.zerops.io/settings/token-management). +2. Create a token and name it for the project, for example `zcp-`. +3. Choose **Custom access per project**. +4. Add exactly one project. +5. Set that project to **Full access** for normal ZCP work. +6. Create the token and copy the value. Zerops shows it only at creation time. +The token's blast radius equals the project and its granted permissions. Other projects, organization settings, and billing stay out of reach. Zerops [Roles & Permissions](/features/rbac#integration-tokens) remain the platform authority. ## Rejected token shapes -ZCP validates the token at startup and refuses wrong shapes before the agent can operate the project. -| Token shape | What ZCP does | Why | -|---|---|---| -| Multi-project, including account-wide full access | Refuses to start | One ZCP process equals one project; ZCP won't pick which one. | -| No project access | Refuses to start | 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 to start | The token no longer authenticates against the Zerops API. | -| Expired or revoked | Refuses to start | The token no longer authenticates against the Zerops API. | -The fix is always the same: generate a per-project token and replace `ZCP_API_KEY` on the surface ZCP reads from. In remote setup, that value is injected into the `zcp` service environment. In local setup, it is the `env` block in `.mcp.json`. +ZCP validates token shape at startup. +| Token shape | What happens | Fix | +| ----------------------------------- | ------------------------------------------- | -------------------------------------------------------------------- | +| Account-wide or multi-project token | ZCP refuses to start. | Generate a token scoped to exactly one project. | +| Token with no project access | ZCP refuses to start. | Grant one project or create a new project-scoped token. | +| Expired or revoked token | ZCP refuses to start or receives HTTP 401. | Replace the token and restart the agent or ZCP process. | +| Read-only project token | Startup may pass, but mutations fail later. | Use full access for normal agent work, or expect read-only behavior. | +| Read-only project token | Startup may pass, but mutations fail later. | Use full access for normal agent work, or expect read-only behavior. | +Common messages: +| Message | Meaning | +| -------------------------------------------------------------- | ------------------------------------------------- | +| `Token accesses N projects; use project-scoped token` | The token can see more than one project. | +| `Token has no project access` | The token authenticates but reaches no project. | +| `No authentication found: set ZCP_API_KEY or log in with zcli` | The `zcp` process did not receive a usable token. | +| `AUTH_TOKEN_EXPIRED` or HTTP 401 | The token expired, was revoked, or is invalid. | +| `AUTH_TOKEN_EXPIRED` or HTTP 401 | The token expired, was revoked, or is invalid. | +For ZCP setup, provide `ZCP_API_KEY`. `zcli` login is a diagnostic fallback, not the normal agent setup path. ## Where the token lives - remote vs local {#where-the-token-lives--remote-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: -| Setup | Where `ZCP_API_KEY` comes from | Who provisions it | -|---|---|---| -| Remote setup | The `zcp` service container env | Zerops platform - injected automatically when the service boots | -| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You - added manually after `zcp init` | -| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You - added manually after `zcp init` | -In remote setup, the service starts with `ZCP_API_KEY` populated. If **Include Coding Agent** is enabled, the bundled agent uses that environment automatically. -In local setup, `zcp init` writes a token-less `.mcp.json` template; you add the `env` block: +ZCP reads `ZCP_API_KEY` from its process environment at startup. It does not write the token somewhere else or exchange it for a derived credential. +| Setup | Where `ZCP_API_KEY` comes from | Who provisions it | +| -------------------------------------------- | ------------------------------------------------------- | -------------------------------------------------------- | +| [Remote setup](/zcp/setup/hosted-workspace) | The `zcp` service environment | Zerops injects it automatically when the service starts. | +| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You add it after `zcp init`. | +| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You add it after `zcp init`. | +In remote setup, do not hand-edit `ZCP_API_KEY`. Replace or rotate it through the Zerops-managed surface so the service keeps the intended project boundary. +In local setup, `zcp init` writes a token-less `.mcp.json`. Add the token manually: ```json { "mcpServers": { @@ -26311,466 +26620,546 @@ In local setup, `zcp init` writes a token-less `.mcp.json` template; you add the } } ``` -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 itself, against the Zerops API | Container env (remote) or `.mcp.json` env block (local) | -| `GIT_TOKEN` | A git push from remote setup to a remote (GitHub, GitLab) | Remote: secret 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 | -| `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 remote setup is the one pushing. In local setup, 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 `zcp` service 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** — an 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. Delivery setup should place it in the CI secret store for the repository that runs the deploy. +Add `.mcp.json` to `.gitignore`. Each project directory should have its own file and its own project token. Switching projects means switching directories, not editing one shared credential. +## Agent credentials are separate +The bundled agent in remote setup may ask you to sign in or provide a model API key. That is not `ZCP_API_KEY`. +Zerops wires the agent to the project through ZCP. It does not provide your model subscription, store your agent login, or rotate your agent provider credentials. Treat the agent account exactly as you would outside Zerops. +## Git and CI credentials +`GIT_TOKEN` matters only when remote setup pushes to a git remote. It authorizes git provider access from the `zcp` service. In local setup, your local git CLI uses your normal SSH key or credential helper, so ZCP does not need `GIT_TOKEN`. +`ZEROPS_TOKEN` is a Zerops API token used by GitHub Actions or another CI system when that system runs `zcli` against Zerops. It is not a GitHub token. Use a separate project-scoped delivery token so ZCP sessions and CI/release workflows can be named, rotated, and audited independently. +For production delivery, `ZEROPS_TOKEN` should be scoped to the production project and stored only in the production CI or release secret store. +With GitHub CLI, the secret shape is: +```bash +gh secret set ZEROPS_TOKEN -b "$ZEROPS_DELIVERY_TOKEN" +``` +Use the GitHub UI or your CI secret manager instead if your team does not allow local CLI secret writes. ## Rotation -Rotate in the Zerops dashboard, then propagate to the consuming surface: -- **Remote setup** — the service env value updates; ZCP picks up the new value the next time the `zcp` service boots or restarts. -- **Local setup** — paste the new token into the `env` block of `.mcp.json`, then restart your agent client 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. | Requires explicit approval in the same conversation, including the service name. Remote setup also blocks deleting the `zcp` service it is running in. | -| **Wholesale service replacement** from an import on a service with prior failed deploy history | First returns a summary of what would be replaced and refuses to continue. The agent must show that evidence before asking you to confirm. | -| **Wholesale service replacement** from an import on a service with prior failed deploy history | First returns a summary of what would be replaced and refuses to continue. The agent must show that evidence before asking you to confirm. | -ZCP itself adds confirmation only for the two operations above. Your agent client or team policy may still prompt before tool calls. Deploys, env changes, lifecycle actions, restarts, scaling, and public-access changes do not get an additional ZCP-specific confirmation gate. Most are reversible by another call. A few are operationally destructive even though they are not gated by ZCP (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. -- **Replace-as-reset** — replacing a service from an import 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. -Threat model and hard refusals: [Trust model](/zcp/security/trust-model). -## Gotchas -- **Multi-project tokens are refused at startup, not at first deploy.** An account-wide 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 in remote setup; `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), and review service-scoped events, logs, deploy results, and git history before approving destructive recovery. +Rotate in the Zerops dashboard, then update the surface that consumes the token. +| Surface | Rotation step | +| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| Remote `ZCP_API_KEY` | Reconfigure or redeploy the remote workspace through the dashboard-managed path, then restart the `zcp` service so the process gets the new value. | +| Local `ZCP_API_KEY` | Paste the new token into `.mcp.json`, then restart the local agent client. | +| `ZEROPS_TOKEN` in CI | Replace the repository or CI secret. The next workflow run uses the new value. | +| `GIT_TOKEN` | Replace the git-provider credential stored for remote setup. | +| `GIT_TOKEN` | Replace the git-provider credential stored for remote setup. | +Rotation is picked up on next process start or workflow run, not in the middle of a live agent session. +## What ZCP enforces for destructive actions +A valid token does not remove every guardrail. ZCP adds explicit confirmation for operations where the loss is not safely reversible from inside the conversation. +| Operation | Gate | +| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Service deletion | Requires explicit approval in the same conversation, including the service name. Remote setup also blocks deleting the `zcp` service it is running in. | +| Wholesale service replacement after failed deploy history | The first request refuses, surfaces what would be replaced, and requires the agent to read failure evidence before asking you to confirm. | +| Wholesale service replacement after failed deploy history | The first request refuses, surfaces what would be replaced, and requires the agent to read failure evidence before asking you to confirm. | +Deploys, env changes, lifecycle actions, restarts, scaling, and public-access changes do not get an additional ZCP-specific confirmation gate. They are normal project operations for a full-access token and should be reviewed through service events, logs, verification output, and team policy. +Approval from a previous chat does not carry forward. A new conversation needs a new approval. +## Evidence before destructive recovery +When a service has recent failure history, ZCP enforces one recovery rule: read the platform evidence before destroying or replacing the service. +The agent should inspect service events, build logs, runtime logs, and failure summaries, then either fix the cause or show you the evidence before asking for destructive confirmation. The point is to preserve the failure context the next session needs. +A service waiting for first code deploy is not the same thing as a failed service. The gate is about recorded failure history, not idle state. +Threat model and boundaries: [Trust model](/zcp/security/trust-model). +## Credential checks +- **Account-wide full-access tokens are refused.** ZCP needs one project, not a broad account credential. +- **`zcp init` regenerates `.mcp.json`.** Re-add `ZCP_API_KEY` after rerunning it. +- **`GIT_TOKEN` is not `ZCP_API_KEY`.** One authorizes git provider access; the other authorizes Zerops project operations. +- **`ZEROPS_TOKEN` in GitHub Actions is not a GitHub PAT.** It is a Zerops API token for `zcli`. +- **A rotated token needs a restart.** The live ZCP process keeps the old environment value until it starts again. +- **A successful confirmation is still destructive.** Backups, git history, and service events are your recovery evidence; ZCP does not auto-rollback a confirmed deletion or replacement. ## 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. -- [Set up local ZCP](/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`. +- [Trust model](/zcp/security/trust-model) - the project boundary this page enforces. +- [Use remote workspace](/zcp/setup/hosted-workspace) - automatic token injection. +- [Set up local ZCP](/zcp/setup/local-agent-bridge) - local `.mcp.json` token setup. +- [Production boundary](/zcp/security/production-policy) - why production gets separate credentials. +- [GitHub integration](/references/github-integration) - CI secret usage with Zerops. ---------------------------------------- # Zcp > Security > Trust Model -The security model starts with one rule: a ZCP process operates one Zerops project. Remote and local setup share that project boundary, but they expose different surroundings to the agent. -## Remote setup or local setup -**Remote setup** is the project-contained path. -- ZCP runs in a `zcp@1` service inside the Zerops project. -- When **Include Coding Agent** is enabled, the bundled agent CLI runs there and is preconfigured to use ZCP. -- It uses a project-scoped token and reaches the project's private network. -- Runtime files are visible only when they are mounted into remote setup. -- Safety profile: **project-contained by design** - no direct access to your laptop, home directory, or other projects. -**Local setup** is the supervise-the-client path. -- The agent runs on your laptop. -- It can touch the same project surface, **plus whatever the agent client can reach on your laptop**. -- It reaches project services over VPN, plus everything else your laptop can already reach. -- Safety profile: **supervise the agent client** - ZCP stays project-scoped, but the agent process inherits your user. -Either path is the right choice in context. Local setup 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 exposing operations. A token with access to no projects is refused; a token with access to multiple projects is refused. The intended token form is 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). | -| 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. +The ZCP trust model starts with one rule: one ZCP process operates one Zerops project. The token decides the project boundary at startup, and ZCP refuses tokens that resolve to no project or multiple projects. +That boundary is strong on the Zerops side. It does not make the agent harmless. A project-scoped token can still deploy, change env vars, restart services, read logs, scale services, and change public access inside that project when Zerops permissions allow it. Treat it like an operations credential for one project. +## Boundary summary +| Question | Answer | +| ------------------------------------ | ------------------------------------------------------------------------------------------- | +| What project can ZCP see? | Exactly one project resolved from `ZCP_API_KEY` at startup. | +| What can ZCP change? | Whatever Zerops RBAC grants that token inside the project. | +| What is outside reach? | Other projects, organization-wide settings, billing, and any operation Zerops RBAC rejects. | +| What network can remote setup reach? | The project's private network from inside the `zcp` service. | +| What network can local setup reach? | The Zerops API directly, plus project-private services only when your laptop VPN is up. | +| Who owns the agent login? | You. The agent subscription or model API key is separate from `ZCP_API_KEY`. | +| Who owns the agent login? | You. The agent subscription or model API key is separate from `ZCP_API_KEY`. | +Zerops [RBAC](/features/rbac) remains the authority. ZCP does not bypass platform permissions; it exposes project operations to the agent only through the token it was given. +## Remote and local blast radius +Remote setup and local setup share the same project boundary, but not the same surroundings. +| Area | Remote setup | Local setup | +| -------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------- | +| Agent location | Inside the `zcp@1` service when **Include Coding Agent** is enabled | On your machine through your editor or CLI agent | +| Files visible to the agent | Workspace files and mounted runtime files | Local files and anything your client permits | +| Private service access | Project-private network without laptop VPN | `zcli vpn up ` from your machine | +| Local machine exposure | No direct access to your laptop | Agent client runs as your local user | +| Best safety posture | Keep ZCP in a development/staging project and avoid mounting unrelated files | Restrict client permissions, shell access, and filesystem scope | +| Best safety posture | Keep ZCP in a development/staging project and avoid mounting unrelated files | Restrict client permissions, shell access, and filesystem scope | +Remote setup is project-contained by design. Local setup is supervise-the-client by design. Either can be the right choice, but local setup requires more attention to your agent client's local permissions. +## Credential ownership +There are three separate credential surfaces people often mix together: +| Credential | Owner | Purpose | +| ----------------------------------- | ---------------------------- | ------------------------------------------------------ | +| `ZCP_API_KEY` | Zerops project token | Lets ZCP operate one Zerops project. | +| Agent subscription or model API key | You / the agent provider | Lets the coding agent run. Zerops does not provide it. | +| Git or CI credentials | You / your repository system | Lets finished work push to git or deploy through CI. | +| Git or CI credentials | You / your repository system | Lets finished work push to git or deploy through CI. | +Remote setup injects `ZCP_API_KEY` into the `zcp` service. Local setup reads it from the `.mcp.json` env block. In both paths, the agent account is still authenticated through the agent's own login flow. +Details: [Tokens and credentials](/zcp/security/tokens-and-project-access). +## What a project-scoped agent can do +Inside the project, the agent can perform normal operational work when the token has permission: +- discover runtime and managed services, +- create or adjust services when the task requires it, +- read and write service env vars, +- deploy app code to runtime services, +- read build logs, runtime logs, and service events, +- restart, reload, stop, start, or scale services, +- enable or disable public access, +- prepare delivery paths such as git-push or CI handoff. +This is why development and staging projects are the right place for ZCP. Production should be a separate project without a `zcp` service; see [Production boundary](/zcp/security/production-policy). ## Remote setup specifics -Remote setup is the project-contained path. A few specifics: -- **`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. -- **Project access stays inside the project.** The `zcp@1` service can read project env and reach project services so the agent can build against real dependencies without you copying credentials around. -- **Filesystem reach is narrower than network reach.** The agent reaches project services over the private network, but only sees runtime files when they are mounted into remote setup. -- **A terminal in remote setup has the same project-scoped access as the agent.** Convenient, and the reason production stays in a separate project. +- **`zcp` service is not the app.** It is the workspace and control surface. Deploys target app runtimes, not the `zcp` service. +- **The service has project-level operational reach.** A terminal in the `zcp` service can use the same project-scoped access as the agent. Review the dashboard's additional changes before deploying the service; they are what give the workspace its project-scoped operating surface. +- **Network reach is broader than file reach.** The workspace can reach project-private services, but it only sees runtime files that are mounted into it. +- **Do not hand-edit `ZCP_API_KEY`.** Remote setup gets the value from Zerops. Manual replacement can break the intended one-project boundary. ## Local setup specifics -Local setup 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; the binary doesn't merge local state across them. -- **VPN setup is outside ZCP's authority.** Bringing up the Zerops VPN requires admin or root approval on your laptop. ZCP can tell 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. -- **Local setup 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: [Set up local ZCP](/zcp/setup/local-agent-bridge). -## Audit surface -Zerops records platform-side evidence: service events, deploy/build results, runtime logs, lifecycle actions, scaling, and public-access changes. It does not record the agent's private reasoning, every shell edit, browser-helper actions, or prompt history outside your agent client. -When taking over from an agent, read evidence in this order: service-scoped events, runtime logs, deploy/verify result, then git history when delivery uses git-push. -## What ZCP refuses by design -ZCP refuses boundaries that should never be inferred: tokens that do not resolve to exactly one project, remote self-deletion, and destructive replacement before the agent has read the relevant failure evidence. Destructive confirmation details live in [Tokens and credentials](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions). +- **The agent inherits local reach.** ZCP is project-scoped, but the local agent client can read files, run commands, and use credentials allowed by your client settings. +- **Each local directory has its own config.** `.mcp.json` and `.zcp/state/` belong to one project directory. Launching from the wrong directory can connect the wrong project or no project. +- **VPN is outside ZCP authority.** Bringing up Zerops VPN needs your operating-system approval. ZCP cannot grant that for the agent. +- **`.env` files are snapshots.** They contain real project credentials and should stay out of git. +## Human confirmation gates +Most project operations do not get an extra ZCP-specific confirmation prompt. Deploys, env changes, restarts, scaling, and public-access changes are normal project operations and are audited through platform evidence. +ZCP adds hard gates where the loss is not safely reversible from the conversation: +| Operation | Gate | +| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| Service deletion | Requires explicit same-conversation approval that names the service. Remote setup also blocks deleting the `zcp` service it is running in. | +| Wholesale service replacement after failed deploy history | Requires the agent to read failure evidence first, summarize what would be replaced, and ask for explicit confirmation. | +| Wholesale service replacement after failed deploy history | Requires the agent to read failure evidence first, summarize what would be replaced, and ask for explicit confirmation. | +Approval from an old chat does not carry forward. A new conversation needs a new approval. +## Audit evidence +Zerops records platform-side evidence. It does not record the agent's private reasoning, every shell edit, browser-helper action, or prompt history outside your agent client. +When taking over from an agent, read evidence in this order: +1. service-scoped events, +2. build logs, +3. runtime logs, +4. deploy and verification output, +5. git history when delivery uses git-push. +| Surface | What it proves | What it does not prove | +| -------------- | ------------------------------------------------------------------------- | -------------------------------------------- | +| Service events | Deploy lifecycle, failures, restarts, scaling, and public-access changes. | The exact source edit that caused the event. | +| Build logs | Dependency install, build commands, compile/package failures. | Runtime request behavior after deploy. | +| Runtime logs | Start crashes, port binding, request-time app errors. | Why the build failed. | +| Verify output | Whether reachability and requested behavior passed. | That unrelated app flows work. | +| Git history | Source changes pushed during git-push delivery. | Uncommitted shell edits. | +| Git history | Source changes pushed during git-push delivery. | Uncommitted shell edits. | +Filter by service hostname when possible. Project-level timelines can include unrelated services and older failures. ## Next steps -- [Tokens and credentials](/zcp/security/tokens-and-project-access) — token scope, storage, rotation, and confirmation gates. -- [Troubleshooting](/zcp/reference/troubleshooting) — recover when the agent or session state drifts. -- [Production boundary](/zcp/security/production-policy) — keep production outside the agent loop. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and confirmation gates. +- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare blast radius before setup. +- [Production boundary](/zcp/security/production-policy) - keep production outside the agent loop. ---------------------------------------- # Zcp > Setup > Choose Workspace -Choose where the ZCP-backed agent runs. ZCP gives the agent the same project-scoped Zerops toolset in both paths, but the filesystem, network path, deploy source, and safety profile are different. -Use remote setup for the first run and project-contained isolation. Use local setup when your checkout, editor, dev server, and git credentials should stay on your machine. -## Short rule -**Remote setup** is the default first run: no local install, project-contained isolation, optional bundled agent, optional browser IDE. -**Local setup** fits when you want to keep your editor, shell, checkout, local dev server, and git credentials. -## Compare the paths -**Remote setup** -- ZCP runs inside the Zerops project in a `zcp@1` service. -- The agent runs inside that service when **Include Coding Agent** is enabled. -- Runtime files can be mounted through SSHFS. -- Managed services are reached over the project-private network. -- Deploy source, env vars, and git credentials live inside the project service. -- Safety profile: project-contained by design. -**Local setup** -- ZCP runs on your laptop as the `zcp` binary. -- The agent runs in your local editor or CLI after `zcp init`. -- App work happens in your local working directory. -- Managed services are reached from your app and shell through Zerops VPN. -- Deploy source and git credentials are your local working directory and local git setup. -- Safety profile: ZCP is project-scoped, but the agent client runs as your user. -## What changes in practice -In remote setup, a `zcp@1` service runs the `zcp` binary inside the Zerops project. Enable **Include Coding Agent** when you want the service to add the bundled agent CLI and preconfigure it to use ZCP. Enable **Cloud IDE** when you also want browser-based VS Code. Runtime files can be mounted into the service, and managed services are reachable over the project-private network. -In local setup, the agent works in your local checkout. You install the `zcp` binary and run `zcp init` in the project folder so the local agent can talk to ZCP. ZCP talks to the Zerops API directly, while your local app and shell need `zcli vpn up` to reach managed services by hostname. -After setup, ZCP reads whether the project has one runtime, a dev+stage pair, or a local checkout linked to a runtime. You usually state the outcome, not the label: dev+stage, one dev runtime, or local deploys to a named runtime. Exact labels live in [Workflow terms](/zcp/reference/agent-workflow#runtime-layouts). -Stage is a review or promotion target, not production. Production should live in a separate Zerops project; see [Production boundary](/zcp/security/production-policy). +Remote setup is the default for most ZCP work. Use local setup when the agent specifically needs to run next to local files, local data, local tools, or a local-only agent client. +Both paths use the same `zcp` binary and the same ZCP model: one Zerops project, live project state, project-scoped operations, and an agent that can finish with proof or a blocker. The choice is where the agent workspace lives. +:::tip Recommended default +**Remote setup** gives you a project-contained agent workspace with clean isolation inside the Zerops project, no local install, project-private networking, and a preconfigured Claude Code + browser VS Code environment when enabled. +::: +## The short version + Remote setup + The agent runs inside a zcp@1 service in the Zerops project. + Use it when you want the agent workspace, project-private networking, and + broad shell permissions contained inside the project. + No local install. + Claude Code and browser VS Code can be bundled. + Project-private services are reachable from the workspace. + Desktop remote editors can connect when you prefer them. + Use remote workspace → + Local setup + The agent runs from your machine. Use it when local files, local data, + your desktop editor, local git credentials, or a specific local agent + client should stay in charge. + Install zcp locally. + Run zcp init in the working directory. + Add a project-scoped token. + Use zcli vpn up for private service access. + Set up local ZCP → +Remote setup usually costs one extra workspace service. Local setup avoids that workspace service, but deploy targets and managed services still cost while running. For most teams, the decision should be about where the agent should safely and ergonomically work, not about the small workspace cost. +:::info Public preview +Remote setup and local setup are both public preview. Local setup has more moving parts and may change faster: binary install path, `.mcp.json`, and `zcp init` artifacts are still settling between releases. +::: +## What changes between the paths +| | Remote setup | Local setup | +| ---------------------- | -------------------------------------------------------------- | -------------------------------------------- | +| `zcp` process | Runs in the Zerops project in a `zcp@1` service. | Runs on your machine. | +| Agent process | Runs in the project workspace when bundled or installed there. | Runs in your local editor or CLI. | +| Files the agent edits | Workspace files and mounted runtime files. | Files on your machine. | +| Private service access | Project-private network from inside Zerops. | Zerops VPN from your machine. | +| Token storage | Platform-injected into the workspace service. | `.mcp.json` in the local project directory. | +| Git credentials | Configured inside the workspace service. | Your local git credentials. | +| Safety posture | Agent shell permissions are contained in the project service. | Agent shell permissions affect your machine. | +| Safety posture | Agent shell permissions are contained in the project service. | Agent shell permissions affect your machine. | +## Starting points +Remote setup can start from: +- **Recipe with AI Agent environment.** A guided path for a known stack. A recipe creates app services, managed services, and the `zcp@1` workspace together. +- **New project with ZCP enabled.** Start from a blank or custom project and add ZCP during project creation. +- **Existing development or staging project.** Add the `zcp` workspace next to services that already exist. Do not add ZCP to a production project; promote verified work through your release path instead. +Local setup can start from: +- **Empty local directory.** The agent creates the app structure from a product request and uses ZCP to select or create Zerops services. +- **Existing project directory.** The app code, editor setup, local data, and git credentials already live on your machine. +- **Recipe prepared for local setup.** The recipe creates the Zerops service baseline, while the agent and files stay local. +## Runtime layout is separate +Remote or local only answers where the agent and `zcp` process run. It does not decide the app runtime layout. +The project can still use: +- one mutable dev runtime, +- a dev + stage pair, +- a single app runtime, +- local files linked to a stage target. +That choice belongs to normal app work: [Build with ZCP](/zcp/workflows/build-with-zcp#choose-the-runtime-layout). +Stage is not production. Keep production in a separate Zerops project and promote work through your release path; see [Production boundary](/zcp/security/production-policy). +## Switching later +You can change setup path later without changing the ZCP project boundary. +- Start remote for evaluation, then move local when local files or tools should own the workflow. +- Start local for a repo-first project, then add remote setup when you want a project-contained agent workspace. +- Keep both paths only when your team understands which source tree is authoritative for deploys. +Before switching, make the handoff explicit: +- Commit, push, or otherwise preserve the source tree that currently owns deploys. +- Choose one deploy source for the next task: the remote workspace/mounted runtime files, or the local project directory. +- Do not edit mounted runtime files and a local repo in parallel unless you have a merge plan. +- Regenerate local `.env` snapshots after project env changes. +- Relink the local deploy target if the runtime hostname changed. ## Next steps -- [Set up remote ZCP](/zcp/setup/hosted-workspace) +- [Use remote workspace](/zcp/setup/hosted-workspace) - [Set up local ZCP](/zcp/setup/local-agent-bridge) -- [Build with ZCP](/zcp/workflows/build-with-zcp) +- [Trust model](/zcp/security/trust-model) ---------------------------------------- # Zcp > Setup > Hosted Workspace -Remote setup is a `zcp@1` service inside your Zerops project. It keeps ZCP, project-scoped credentials, private-network access, and optional agent tooling inside the project boundary. -Enable **Include Coding Agent** when you want Zerops to add the bundled agent CLI and preconfigure it to use ZCP in that service. Enable **Cloud IDE** when you also want browser-based VS Code for watching or intervening. -After provisioning, the service is ready for your first app instruction. If **Cloud IDE** is enabled, you can open the browser workspace from the dashboard; otherwise use the access method configured for the service. -Remote and local setup have the same ZCP project boundary but different work surfaces. The comparison lives in [Choose remote or local setup](/zcp/setup/choose-workspace); the broader human development modes live in [Local & Remote Development](/features/local-remote-development). -## Two paths to remote setup -Zerops provisions the service for you - no local install, no MCP config to write by hand, 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 a `zcp@1` service with **Include Coding Agent** enabled. -- **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 base result: a `zcp@1` service inside the project with `ZCP_API_KEY` injected automatically and scoped to this project. **Include Coding Agent** adds the preconfigured agent. **Cloud IDE** adds the browser editor. -## Path A — recipe + AI Agent environment -Recipes are the quickest path when their stack matches the runtime layout 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@1` service with **Include Coding Agent** enabled 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 with a bundled agent is added. 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**: +Remote setup gives you an agent workspace inside the Zerops project. +The `zcp@1` service runs the same `zcp` binary used by local setup, but the agent, terminal, and optional browser IDE run in a clean project service instead of on your laptop. That is the main advantage: Zerops can provide a low-friction agent workspace with project-private networking and broader shell permissions while keeping that blast radius inside the project. +Use this path when you want a project-contained workspace, a safer default boundary for broad agent permissions, or a preconfigured environment with Claude Code and browser VS Code. +App code still deploys to your app runtime services. The `zcp` service is the workspace and control surface; it is not the application runtime. +## What you get +- **`zcp@1` workspace service.** Runs ZCP inside the project and gives the agent project-scoped operations. +- **Platform-injected `ZCP_API_KEY`.** Zerops injects the project token into the workspace; you normally do not set it by hand. +- **Bundled Claude Code when enabled.** The **Include Coding Agent** option installs and preconfigures Claude Code. You authenticate with your own Claude subscription login or API key. More bundled agents are planned. +- **Browser VS Code when enabled.** The **Cloud IDE** option gives you a browser editor, terminal, and a place to supervise or take over the agent session. +- **Project-private networking.** The workspace can reach managed services by hostname without laptop VPN. +- **Open workspace model.** You can add other agent CLIs, private MCP servers, helper processes, dotfiles, package installs, or a derived team image. +For the local alternative, see [Set up local ZCP](/zcp/setup/local-agent-bridge). For the tradeoffs, see [Remote or local ZCP setup](/zcp/setup/choose-workspace). +## Choose a starting path +### First-time trial +Use the [Quickstart](/zcp/quickstart) when you want the guided recipe path. It covers the recipe catalog, **AI Agent** environment, Claude Code authentication, Browser VS Code, first product prompt, and proof. +After provisioning, continue with a product prompt in [Build with ZCP](/zcp/workflows/build-with-zcp). +### Recipe with AI Agent environment +Use this when you want a guided stack baseline for a new development or staging project. A recipe with an **AI Agent** environment creates app services, managed services, and the `zcp@1` workspace together. +Keep **Coding Agent** enabled when you want bundled Claude Code. Keep **Cloud IDE** enabled when you want Browser VS Code in the project. +### New project with ZCP enabled +Use this when you want a blank or custom 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. Keep **Include Coding Agent** enabled to add and preconfigure the bundled agent inside the service. Use **Configure** when you need to choose authentication or adjust Cloud IDE access; otherwise leave the defaults 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 service -Open the `zcp` service from the Zerops dashboard after it reaches running state. +2. Enter the project name, region, and tags. +3. Enable **Add Zerops Control Plane (ZCP) service**. +4. Keep **Include Coding Agent** enabled if you want bundled Claude Code. +5. Keep **Cloud IDE** enabled if you want browser VS Code. +6. Create the project. +This gives you a project with the `zcp@1` workspace. App runtimes and managed services may still be created later by normal ZCP app work. +### Existing development or staging project +Use this when a development or staging project already has runtime or managed services. Do not add `zcp` to a production project; create or use a separate development/staging project and promote verified work through the release path. +Add a `zcp` service from the project dashboard the same way you add another Zerops service. The workspace appears next to the existing services and receives project-scoped ZCP access from the platform. +The agent should still read project state before changing anything. Existing services are context, not automatic instructions for the next task. +## Open the workspace +For a returning remote workspace, open the existing `zcp` service: 1. Open the project in the [Zerops dashboard](https://app.zerops.io/). 2. Open the `zcp` service. -3. If **Cloud IDE** is enabled, open the service workspace URL. Code-server launches in a new tab. If the service is exposed on a `.zerops.app` subdomain, see [Public access](/features/access) for the routing model. -If **Cloud IDE** is disabled, remote setup can still exist as a project-contained service, but there is no browser editor to open. Use the access method configured for the service, such as SSH, or enable Cloud IDE when you want a browser workspace. -## Run the agent -When **Include Coding Agent** is enabled, the bundled agent starts already connected to this project through ZCP. In the Cloud IDE path, the terminal has the bundled agent available with `ZCP_API_KEY` in its environment. -For a connection sanity check, ask the bundled agent: -```text -List the services in this project and tell me which are runtimes versus managed dependencies. -``` -A working setup answers with the runtime services (for example `appdev`, `appstage`) and managed services (for example `db`), and says whether the project has one runtime or a dev+stage pair. -For real work, ask for the app behavior, not the Zerops procedure: -```text -Build a task board where tasks stay saved after refresh. -``` -Discovery, service setup, deploy, verification, and final reporting happen behind that app request. -If **Include Coding Agent** is disabled, the `zcp@1` service exists but no preconfigured agent is waiting inside it. Enable the option or use [local ZCP](/zcp/setup/local-agent-bridge) from your own agent client. -After the first setup pass, runtime services can be SSHFS-mounted into the service filesystem with one folder per runtime. 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 remote setup. -If you added ZCP to an existing project, the agent should inspect existing services and use them instead of creating duplicates. That is handled as part of the first product prompt; the details are explained in [Service setup behind the prompt](/zcp/workflows/create-or-adopt-services). -When Cloud IDE is enabled, the browser workspace is persistent. Close the browser tab and reopen the workspace link later; editor state, open files, and the agent session survive across reconnects as long as the `zcp` service is running. -## If connection fails -If the agent can't reach ZCP, the service may still be starting. Wait for it to reach running state in the dashboard and reload the workspace. If **Include Coding Agent** was not enabled, add it or use local ZCP from your own agent client. -At this point, the agent can receive the first app instruction. The [Quickstart](/zcp/quickstart) shows the shortest end-to-end run. -## Customize remote setup -Remote setup is a normal Zerops service, so teams can customize it like one: add tools, preload team config, or run helper processes next to the agent and Cloud IDE. Keep this separate from app runtime work; deploy app code to runtime services, not to `zcp`. -## Gotchas -- **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. If the agent targets `zcp` as the app runtime, stop and correct the target. -- **Don't set `ZCP_API_KEY` by hand in remote setup.** It's platform-injected. Manual override risks breaking the agent's access. (Local setup 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 the bundled agent. Until the service reaches running state, the workspace URL may return an empty page or error. +3. Use **Browser VS Code** when **Cloud IDE** is enabled. +4. Complete the Claude Code login or API-token flow if prompted. +After authentication, Claude Code is connected to this project's ZCP service and ready to work from the browser VS Code terminal. If the workspace was already authorized, use this same path to resume or supervise work. +First load can take a minute while the image, Cloud IDE, and bundled agent finish starting. If the page opens before the service is ready, wait until the service is running and reload. +## Use your own editor or tools +Browser VS Code is the quickest entry point, not the only one. +Editors that support remote development can connect to the workspace or runtime services over SSH, depending on how your team wants to work. Common options include VS Code Remote SSH, JetBrains Gateway, Cursor, Zed, and plain SSH. +This keeps the agent and project-private network inside Zerops while letting you use a desktop editor UI. Broader editor patterns live in [Local & Remote Development](/features/local-remote-development#native-ide-over-ssh). +You can also install another agent CLI or additional MCP servers inside the `zcp` service. The bundled Claude Code flow is a convenience, not a closed product boundary. +## Advanced customization guardrails +Tools installed inside the `zcp` service may see workspace environment variables, project-private networking, and any runtime files mounted into the workspace. Use trusted and pinned tools, and keep model credentials, git delivery tokens, external API keys, and production credentials out of the workspace unless the current task explicitly needs them. +## What belongs where +| Concern | Where it belongs | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| Agent workspace | The `zcp@1` service: agent CLI, browser VS Code, shell tools, MCP servers, helper processes, dotfiles. | +| App code deploys | Your app runtime services; not the `zcp` workspace service. | +| ZCP project token | Injected by Zerops into the `zcp` workspace. | +| Agent account | Your Claude subscription login or model API credential. Zerops wires the agent to ZCP, but the agent account remains yours. | +| Git credentials | Configured inside the workspace when the agent should commit or push from remote setup. | +| Production promotion | A separate production project and release path. | +| Production promotion | A separate production project and release path. | +## Runtime file access +Remote setup can mount runtime service filesystems into the `zcp` workspace after the first setup pass. Each mounted runtime appears as its own folder. Editing a mounted file changes the file inside that runtime service, with no upload step from your laptop. +Filesystem reach is narrower than network reach. The workspace can reach project services over the private network, but it only sees runtime files that are mounted into it. +## Make customization persistent +The `zcp@1` service is a normal Zerops service. One-off shell installs disappear when the service is rebuilt unless you make them part of the service setup. +Use these patterns: +- **Small additions:** put package installs, dotfiles, or bootstrap scripts into service init commands. +- **Team-standard workspace:** build a derived image based on `zcp@1` with required tools already present. +- **Helper processes:** run project-local helpers next to the agent and editor workspace when your team needs private integrations. +Keep app runtime build steps with the runtime services. The remote workspace carries the agent, tools, and project access; runtime services own app builds and deploys. +## Next steps +- [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - how `ZCP_API_KEY`, git credentials, and CI secrets differ. +- [Production boundary](/zcp/security/production-policy) - why production belongs in a separate project. ---------------------------------------- # Zcp > Setup > Local Agent Bridge -Local setup runs the `zcp` binary on your laptop, initialized in one project directory. The agent edits your checkout, your tools run the dev server, and ZCP deploys from your working directory to the linked Zerops runtime. -`zcp init` writes the local MCP configuration for that folder, so a compatible local agent client can talk to ZCP as the `zerops` MCP server. With `zcli vpn up `, your laptop also joins the project's private network - the same network your services use. -Local setup is newer than remote setup. Expect install details and generated project files to move between releases. -Use this path when you already have a comfortable local setup - your editor, your shell, your dev server - and want a coding agent that can drive Zerops from the same machine. If you're evaluating ZCP for the first time, [remote setup](/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. ZCP is what you add when you also want a coding agent operating the project from the same machine: discovering services, generating local env snapshots, deploying, reading logs, verifying. -Security note: ZCP itself stays project-scoped, but the agent client runs as your local user. Set your client permissions accordingly. +Local setup runs ZCP on your machine. +Use it when the agent should work next to local files, local data, your desktop editor, your terminal tools, and your local git credentials. ZCP still scopes Zerops operations to one project, but the agent client itself runs as your local user. Your local agent permissions and filesystem allowlists matter. +[Remote setup](/zcp/setup/hosted-workspace) is the safer default when you do not need local files or tools. Use local setup when the local workflow is the point. +:::warning Public preview +Local setup has more moving parts than remote setup and may change faster. The binary install path, `.mcp.json` shape, and files written by `zcp init` are still settling between releases. +::: +## Choose a local starting point +Pick the folder that should own app work: +- **Empty local directory.** Start with no app code yet. The agent can create the app structure and use ZCP to select or create Zerops services. +- **Existing project directory.** Use this when app code, local data, editor setup, test fixtures, and git credentials already live on your machine. +- **Recipe prepared for local setup.** Use a recipe to create the Zerops service baseline, then run the agent locally from the directory that should own source changes. +After that choice, the mechanics are the same: install `zcp`, run `zcp init`, add a project-scoped token, start VPN when private service access is needed, and link a deploy target when the agent should deploy. +## What local setup gives the agent +- **Local files as source.** The agent edits the directory on your machine, and deploys use that working directory. +- **Your editor and terminal.** Framework CLIs, test runners, local data, and local feedback stay under your normal tools. +- **Your git credentials.** Pushes use your local git CLI, SSH agent, or credential helper. +- **Project-scoped Zerops operations.** ZCP lets the agent discover services, generate env snapshots, deploy to linked runtimes, read logs, and verify the project. +- **Private service access through VPN.** Your local app and shell reach project services through `zcli vpn up`. +Security note: local ZCP cannot protect your laptop from the agent client. Configure your local agent approvals the same way you would for any coding agent running on your machine. ## 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 compatible local agent client installed and logged in. ZCP doesn't see the agent client's subscription or login credentials. -## Get a project-scoped Zerops token -ZCP needs a Zerops API token scoped to **one project**. Multi-project tokens, including account-wide full-access tokens, are refused at startup. Generate one in the Zerops dashboard, then keep the value handy for the local MCP configuration step. Full walkthrough, rejection rules, and rotation: [Tokens and credentials](/zcp/security/tokens-and-project-access). -## Install `zcp` +- A Zerops project. Create one from the [Zerops dashboard](https://app.zerops.io/dashboard/project-add), from a [recipe](https://app.zerops.io/recipes), or use an existing development/staging project. +- [zCLI](/references/cli) installed and authenticated on your machine. +- A compatible local agent client installed and logged in. +- A **project-scoped Zerops token** for exactly one project. Multi-project tokens are refused at startup. +- A local directory where the agent should run. +You do not need ZCP just to develop locally against Zerops services as a human. `zcli vpn up` plus your editor is enough for that. Add ZCP when a local coding agent should also understand and operate the Zerops project. +## 1. Get a project-scoped token +ZCP needs a Zerops API token scoped to exactly one project. For normal agent work, use a single-project token with full access to that project. Read-only tokens can authenticate but fail on deploys, env changes, lifecycle actions, and other mutations. +Token generation, rejected shapes, and rotation are covered in [Tokens and credentials](/zcp/security/tokens-and-project-access). Keep the token value available for the `.mcp.json` step below. +## 2. Install `zcp` ```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. -## Run `zcp init` in your project folder -In a terminal in your project's working directory: +The installer downloads the latest release for your platform into `~/.local/bin`, or `/usr/local/bin` when run as root. Verify the install: +```bash +zcp version +``` +If your shell cannot find `zcp`, add the install directory to `PATH` and reload the shell. +## 3. Run `zcp init` +From the local directory the agent should operate: ```bash zcp init ``` -`zcp init` writes the local MCP configuration and agent instructions for this project. Re-running it may refresh generated config; after rerunning it, confirm `.mcp.json` still contains `ZCP_API_KEY` before launching the agent. -## Configure `zcp` as the local MCP server -`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 your agent client from the 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--remote-vs-local). -The agent client starts and lists the MCP servers it found. The `zerops` server should be in the list; that is the local ZCP connection. -### Sanity check +`zcp init` writes the local MCP configuration and agent instructions for this project directory. +Current generated artifacts: +- `.mcp.json` - MCP configuration your local agent client reads to discover ZCP. +- `CLAUDE.md` - agent instructions for operating ZCP in this project. +- `.claude/settings.local.json` - Claude Code per-project settings when that client is used. +- `~/.config/zerops/aliases` plus a shell-rc sourcing line - helper aliases for launching the agent from this project. +- `.zcp/state/` - created later when ZCP first writes local project state. +Re-running `zcp init` may refresh generated config. `CLAUDE.md` preserves edits outside managed markers, but `.mcp.json` is regenerated from the token-less template. If you rerun it, re-check the `ZCP_API_KEY` env block before launching the agent again. +## 4. Add `ZCP_API_KEY` +`zcp init` writes a token-less `.mcp.json`. Add the project token under the `env` block: +```json +{ + "mcpServers": { + "zerops": { + "command": "zcp", + "args": ["serve"], + "env": { + "ZCP_API_KEY": "" + } + } + } +} +``` +Add `.mcp.json` to `.gitignore`. It contains a live project credential and should not leave the machine. Each project directory should have its own `.mcp.json` and its own project token. +The server name `zerops` is intentional. Do not rename that key unless your agent client explicitly requires a different name and you understand the resulting prompt/instruction changes. +## 5. Launch the agent from the project root +Start your local agent client from the same directory that contains `.mcp.json`. The client should list `zerops` as an available MCP server. +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 the agent client from the wrong directory. -## Connect private services with `zcli vpn up` -Your laptop reaches managed services (databases, caches, storage) through the Zerops project [VPN](/references/networking/vpn). ZCP 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: +A working connection answers with the project's runtime and managed services. If the agent cannot reach ZCP, check the launch directory, token scope, and whether the client loaded `.mcp.json`. +## 6. Bring up VPN when private services are needed +ZCP can talk to the Zerops API without VPN. Your local app, shell, tests, database clients, and framework commands need VPN to reach project-private service hostnames such as `db` or `cache`. ```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. -## Local env bridge -When a local app needs project credentials, ask for the product change and let ZCP generate the local env bridge as part of that work. For a standalone sanity check, you can ask: +VPN setup needs admin or root approval on macOS and Linux. ZCP can tell the agent which command is needed, but it cannot approve or start the VPN for you. +After the tunnel is up, project service hostnames resolve from your machine. Laptop sleep, network changes, and idle time can drop the tunnel; run the command again when local service connections start failing. +## 7. Generate local env when needed +When the local app needs project credentials, ask the agent for the app work and let ZCP generate the env bridge as part of that work. For a standalone check: ```text Use ZCP to generate a .env file for my local app. ``` -ZCP resolves the project env references your app would see when deployed and writes a `.env` snapshot in your working directory. Your local app uses the same hostnames and credentials, reached over VPN. Regenerate after changing project env variables. -## Linked deploy target -Local setup needs one Zerops runtime linked as the deploy target, often a stage runtime, 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. Ask the agent to link a runtime by hostname when ready. -With a linked runtime, ZCP can deploy from your working directory. Without one, it can still inspect the project and generate env snapshots, but deploys need a target runtime first. -## 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. Remote ZCP can give the agent SSHFS access; local ZCP 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; the agent client 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 the agent client both expect it. -- **The `.env` is a snapshot.** Regenerate after changing project env variables, not before. +ZCP resolves the env references your app would see when deployed and writes a `.env` snapshot in the working directory. The file is a snapshot, not a live sync. Regenerate after changing project env variables. +Keep `.env` out of git. It contains real project connection values. +## 8. Link a deploy target +Local setup needs a Zerops runtime when the agent should deploy from your working directory. A stage runtime is the usual target because it lets local work prove itself before any production promotion path. +If the project has exactly one runtime, ZCP can use it automatically. If multiple runtimes exist, the agent should ask which one to link. Answer by hostname, for example: +```text +Link this local directory to appstage for deploys. +``` +Without a linked runtime, ZCP can still inspect services and generate env snapshots, but deploys from your local directory need a target first. +## What stays outside ZCP +ZCP does not replace your local feedback loop. Vite, Valet, Docker Compose, your IDE runner, framework CLIs, local fixtures, and test data stay under your normal local tooling. +ZCP does not mount Zerops runtime filesystems on your laptop. Remote setup can mount runtime files into the workspace; local setup works from files on your machine. If you need to inspect runtime files from your machine, use [SSH](/references/networking/ssh) directly. +ZCP does not own your git credentials. In local setup, your local git CLI, SSH agent, or credential helper handles pushes. +## Local setup checks +- **Launch directory matters.** Start the agent from the folder that contains `.mcp.json`; otherwise it may connect to no ZCP server or the wrong project. +- **Multi-project tokens are rejected.** Use a token scoped to exactly one project. +- **`zcp init` can remove the token from `.mcp.json`.** Re-add `ZCP_API_KEY` after rerunning init. +- **VPN is separate from ZCP auth.** The agent may reach ZCP while your app still cannot reach `db` because VPN is down. +- **`.env` is a credential snapshot.** Regenerate after project env changes and keep it out of git. - **Production stays out of this loop.** Local ZCP is for development and staging projects. ## Next steps -- [Deploy, verify, and fix](/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 remote or local setup](/zcp/setup/choose-workspace) - remote vs local and runtime layouts. - ----------------------------------------- - -# Zcp > Workflows > Build And Verify App - -After the target runtime and dependencies are known, the agent can change code and `zerops.yaml`, deploy the runtime, fix failures from evidence, and verify the result. -A ZCP task is done only when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the working URL or a concrete blocker. -## What the agent does -ZCP treats deploy as diagnostics and verification as the completion gate. The user does not have to script this sequence; it is the expected path behind an app prompt. -- **Runtime scope.** The agent identifies which runtime changed. -- **Code and `zerops.yaml`.** The agent changes app files and runtime config where needed. -- **First deploy.** The first verified runtime deploy goes through ZCP. -- **Runtime reachability.** The agent checks that the runtime answers on the real platform path. -- **Requested behavior.** The agent proves the endpoint, URL, UI state, row, job result, or other acceptance evidence. -- **Evidence-based fix.** If either verification layer fails, the agent reads new logs/events/check output, fixes the cause, and redeploys. -A green build or running runtime is not done until the requested endpoint, UI, or state is checked on the real URL. Retries should use new evidence, not repeat the same deploy. -## Runtime scope is a precondition -Before editing, the agent should make the runtime service visible: `appdev`, `appstage`, `app`, or the linked runtime in a local setup. Managed services such as databases and caches are dependencies, not deploy targets. -If the agent starts writing files before it knows the runtime scope, treat that as a correction signal: it should read ZCP status and identify the target before continuing. -## Deploy -The first deploy uses the direct ZCP deploy path. Delivery choices such as git-push or external handoff apply after a verified deploy exists. -Deploy failures point to different evidence: -| Failure point | Read first | -|---|---| -| Build failed | Build logs and `zerops.yaml` build steps | -| Runtime started then crashed | Runtime logs and start command | -| App is up but route fails | Runtime logs at request time and behavior check output | -| Local app cannot reach managed services | VPN state and generated `.env` values | -| Local app cannot reach managed services | VPN state and generated `.env` values | -## Verify -Verification has two layers: -| Layer | What it proves | -|---|---| -| Platform reachability | The service is running, recent error logs are checked, and HTTP runtime services answer a probe when eligible. | -| Requested behavior | The thing you asked for works: endpoint body, UI state, database row, job result, or stage URL. | -| Requested behavior | The thing you asked for works: endpoint body, UI state, database row, job result, or stage URL. | -Both layers must pass before the agent reports completion. -## Fix or stop -The agent should name the failure point in plain language, read fresh logs/events/check output, fix the indicated cause, and redeploy. A repeated retry with no new evidence is not progress. -Stop and ask for a decision when scope is ambiguous, credentials are missing, destructive recovery is needed, or several evidence-based retries hit the same failure. -## Review and take over -The platform records deploys, events, runtime logs, and lifecycle actions. It does not record every agent thought, browser click, or shell edit. When taking over, ask for ZCP status first, then read service-scoped events, logs, deploy/verify results, and git history if delivery uses git-push. -## Gotchas -1. **Deploy success is not verify success.** A green build with a broken feature is not complete. -2. **Stage is explicit.** In dev+stage projects, the agent should say when stage is included before changing or verifying it. -3. **Secrets belong in env vars.** If the app needs a third-party API key, the agent should ask for an env-var path, not put secrets in source. +- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare setup paths. +- [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and destructive confirmations. ---------------------------------------- # Zcp > Workflows > Build With Zcp -Start with what the app should do, not with tool calls or workflow names. +Build with ZCP starts from product intent, not from an operations checklist. ```text -Build a task board where tasks stay saved after refresh. -``` -Behind that prompt, the agent should inspect the project, choose a target runtime, make the app change, and use real deploy evidence until the requested behavior is proven. ZCP supplies the project knowledge and tool access needed for that loop: live services and env state, Zerops guidance, deploys, logs, verification, and guarded infrastructure changes. -Do not make the prompt longer to restate completion expectations such as deploy, verification, or returning the URL. The words you add should change the app, the stack, the runtime layout, the acceptance criteria, or the delivery path. -You also do not need to paste an infrastructure inventory, env wiring plan, or log summary into the prompt. ZCP makes those available to the agent from the live project. -## What happens after your prompt -The agent should make this path visible as it works: -**Project discovery.** Read what exists now: runtime services, managed services, env vars, logs, recent events, and current work state. -**Service setup.** Use existing services when they fit, or create the missing runtime and managed dependencies before app code starts. -**Runtime scope.** Identify the app runtime that will receive code, such as `appdev`, `appstage`, `app`, or a linked local deploy target. The `zcp` service is never the app target. -**Code, deploy, verify, fix.** Change app files and `zerops.yaml`, deploy through Zerops, verify reachability and requested behavior, read evidence on failure, and retry from the cause. -**Delivery after proof.** After a verified deploy exists, keep direct deploy, push to git, or use external handoff to CI or a human. -Advanced export/reuse path: [Package a running service](/zcp/workflows/package-running-service). +Build a task board. +Tasks should stay saved after refresh. +``` +Behind that prompt, ZCP gives the agent a development surface for one Zerops project: settle the runtime layout when it is unclear, work from live project context and Zerops-specific guidance, then close according to the delivery preference after proof. +Prompt for the outcome, constraints, acceptance criteria, runtime layout, and delivery preference when they matter. ZCP treats deploy, verification, evidence reading, and proof or blocker reporting as part of completion. +## The lifecycle + Product prompt + Build a task board... + 1. Runtime layout + Settle the app runtime and dependencies. + Existing services + Missing services + Layout choice + 2. Development context + Give code-facing work live project state. + Live project state + Relevant knowledge + Deploy + verify + 3. Delivery preference + Close app work after proof or a clear blocker. + Proof first + Keep direct deploy + Git, CI, or handoff +The important user-facing controls are the runtime layout you want, the product or code change you ask for, and the delivery preference that should apply after the work is proven. You do not run the flows by hand; ZCP uses them to give the agent the right context and done criteria. +## 1. Settle the runtime layout {#choose-the-runtime-layout} +When the project does not yet have a clear app runtime and dependency set, ZCP prepares the project layout before app feature work starts. It reads current project state, uses existing services when they fit, creates missing app runtimes or managed services when needed, and stops when the agent knows where app code belongs. +The user-facing choice that most affects this first step is the runtime layout. You can let ZCP infer it from the project, or you can name the layout in the prompt when it matters. +| Runtime layout | Use when | What to tell the agent | +| ------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------- | +| **Dev** | You want one mutable runtime for fast iteration, experiments, or early app work. | `Build a small Node.js API on dev.` | +| **Dev + stage** | You want a development runtime plus a separate runtime for review or promotion rehearsal. | `Build a Node.js API with PostgreSQL on dev+stage.` | +| **Stage / linked target** | You work from local files or a single target runtime and want ZCP to use that target. | `Use appstage as the deploy target for this local app.` | +| **Stage / linked target** | You work from local files or a single target runtime and want ZCP to use that target. | `Use appstage as the deploy target for this local app.` | +If you do not specify a layout, the agent should infer from the existing project and ask only when the choice changes cost, runtime layout, stage scope, credentials, or a destructive action. +The layout is about app runtimes, not where ZCP itself runs. Remote setup can use any of these layouts from the `zcp@1` workspace. Local setup usually works from local files into a linked stage or app runtime. +Managed services such as PostgreSQL, Valkey, queues, search, storage, or mail are dependencies. ZCP gives the agent their state and wiring patterns, but app code deploys to runtime services. +## 2. Develop with live project context {#develop-with-live-project-context} +Any instruction that implies interaction with code starts or continues app development: build, inspect, change, fix, continue, deploy, or verify app work. ZCP wraps that work with the project context the agent needs, but it does not replace the normal coding conversation between you and the agent. +Inside app development, the agent can still read files, make implementation choices, run tools, and follow your product instructions. ZCP matters around that work: it supplies current project state, relevant Zerops knowledge for the services in the project, scoped operations, deploy evidence, verification checks, and recovery rules. +| What the agent gets from ZCP | Why it matters during development | +| ---------------------------- | ------------------------------------------------------------------------------------------------------- | +| Live project state | Services, runtime layout, env-var keys, Zerops references, events, deploys, logs, and saved work state. | +| Zerops knowledge | Runtime and managed-service wiring, `zerops.yaml`, env refs, ports, public access, and deploy behavior. | +| Project-scoped tools | Operations for service setup, env vars, deploys, logs, lifecycle, verification, and recovery. | +| Workflow instructions | When to use existing services, when to create missing ones, when to read evidence, and when to ask. | +| Workflow instructions | When to use existing services, when to create missing ones, when to read evidence, and when to ask. | +That means you usually do not paste an infrastructure inventory, env wiring plan, or log summary into the chat. A short product prompt can be enough because the agent can ask ZCP what exists now. +What you control here is the product request, stack preference, acceptance criteria, and any instruction to inspect or continue existing work. ZCP gives the agent the deploy, verification, evidence reading, and recovery loop behind that request. +Expect the agent to stop for external credentials, destructive actions, cost-affecting infrastructure, ambiguous runtime or stage choices, and product decisions it cannot infer. +| Development input | Use when | What to tell the agent | +| ----------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------- | +| **Product behavior** | The feature or app outcome matters more than the exact stack. | `Build a task board where tasks stay saved after refresh.` | +| **Stack preference** | You want a specific runtime, framework, or managed service. | `Use Laravel and the existing PostgreSQL service.` | +| **Acceptance criteria** | A specific behavior must be true before the task is done. | `A user can create a task, refresh the page, and still see it.` | +| **Runtime layout** | The target layout matters for the work or review path. | `Build a Node.js API with PostgreSQL on dev+stage.` | +| **Existing work** | A previous ZCP session stopped or the project already exists. | `Read ZCP status first, then continue the interrupted task board work.` | +| **Existing work** | A previous ZCP session stopped or the project already exists. | `Read ZCP status first, then continue the interrupted task board work.` | +## 3. Choose delivery preference {#choose-delivery-after-proof} +Delivery preference is how app work closes after there is a verified result. You can include it in the original prompt or set it later. +The first functional deploy is still direct so the agent can prove the app actually runs. Delivery preference decides what happens after that proof and how later ZCP sessions should finish similar work. +| Delivery preference | What it means | What to tell the agent | +| ---------------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| **Keep direct deploy** | ZCP keeps deploying directly to the target runtime for fast dev/stage iteration. | `Keep direct deploy for now.` | +| **Push to git** | The agent commits and pushes working changes to the configured repository. | `When the app works, push changes to git@github.com:my-org/task-board.git.` | +| **CI / handoff** | A repository integration, GitHub Actions workflow, release process, or human owns the next deploy. | `Set up GitHub Actions delivery for future deploys after the app works.` | +| **CI / handoff** | A repository integration, GitHub Actions workflow, release process, or human owns the next deploy. | `Set up GitHub Actions delivery for future deploys after the app works.` | +ZCP records the delivery choice so later work can follow the same path without re-explaining the handoff every time. Git push credentials, CI secrets, and production credentials are separate from `ZCP_API_KEY`; see [Tokens and credentials](/zcp/security/tokens-and-project-access). +Production should still be a separate Zerops project. ZCP can prepare the handoff from dev or stage, but production promotion belongs to your release path; see [Production boundary](/zcp/security/production-policy). ## What the final answer should contain For a completed app task, the agent should report: -- which runtime service changed, -- which deploy passed, -- which URL, endpoint, or UI state proved the requested behavior, -- any env vars, managed services, or delivery settings touched, -- the delivery choice if one was set. -If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the next decision needed from you. - ----------------------------------------- - -# Zcp > Workflows > Create Or Adopt Services - -When an app prompt needs infrastructure, ZCP should discover what already exists before creating runtimes or managed services. This usually happens because you asked for a product outcome, not because you explicitly asked for infrastructure. -For example, `Build a task board where tasks stay saved after refresh.` may require an app runtime and database. `Use the existing Laravel services` tells the agent to reuse what is already there instead of creating duplicates. -Behind that prompt, the agent should answer: -- Which runtime service will hold app code? -- Which managed services does the app depend on? -- Do those services already exist, or should the agent create them? -- Are they running and visible to ZCP? -This part does **not** write application code, create `zerops.yaml`, run the first deploy, or verify behavior. Those belong to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). -## Starting situations -| Situation | What should happen | -|---|---| -| Runtime services already exist | Use them. Identify runtimes and managed dependencies. Do not recreate or rename services just to begin. | -| Project has only the `zcp` service | Treat it as empty from the app point of view. Create or import the requested service layout. | -| Project is empty and request matches a known stack | A recipe may be a good starting base, but the starter still has to be changed into the requested app. | -| Project is empty and request needs a custom layout | Create a custom service plan before app work starts. | -| Setup was interrupted | Resume from current ZCP status instead of starting over. | -| Setup was interrupted | Resume from current ZCP status instead of starting over. | -The `zcp` service is the ZCP setup, not the application runtime target. -## Prompt examples -Product prompts can imply the service setup; they do not need to spell it out: -```text -Build a Node.js API with PostgreSQL. Use the existing dev+stage pair if this project has one. -``` -```text -Use the existing Laravel services and build a small notes app. -``` -```text -Add saved tasks to this task board. -``` -The agent should explain whether it used existing services or created new ones, then move into app work when infrastructure is known. -## Done state -Service setup is done when the app runtime and managed dependencies are known, any new services are visible, and the agent is ready to start app code, `zerops.yaml`, deploy, and verification work. -When files, framework setup, deploys, logs, or behavior checks appear, the work has moved to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). -## Failure signals -| Symptom | What to do | -|---|---| -| Agent targets `zcp` as the app | Correct the target to the runtime service. | -| Agent recreates existing runtimes | Ask it to inspect current services and use what exists. | -| Agent writes app files during service setup | Stop and finish infrastructure first, or move explicitly to app work. | -| Service creation fails | Treat it as a hard stop until the failure is understood. | -| Service creation fails | Treat it as a hard stop until the failure is understood. | - ----------------------------------------- - -# Zcp > Workflows > Delivery Handoff - -Delivery is decided after the app works. You can include delivery intent in the original product prompt, but ZCP applies it only after a verified deploy exists. The first deploy uses ZCP's direct deploy path; the delivery choice records how future changes should be delivered. -## Three delivery choices -**Keep direct deploy** for fast iteration, solo projects, and demos. The agent keeps deploying through ZCP directly. -**Push to git** when you want commits, reviews, or a repository-driven build. The agent commits and pushes to a configured remote; a build integration may run afterward. -**External handoff** when your CI, release process, or a human owns delivery. ZCP records what happened but does not initiate future deploys. -You can ask in plain language: -```text -After the app verifies, set up git-push delivery to git@github.com:my-org/task-dashboard.git. -``` -```text -Keep ZCP deploying directly for now. -``` -```text -My CI takes over after this verified deploy. -``` -## Push to git -Git-push needs committed code, a remote URL, and credentials that can push. -Remote and local setup differ: -- Remote setup uses credentials configured inside the `zcp` service, commonly a fine-grained git token. -- Local setup uses your local git credentials and working directory. -A push is not proof that the app works. If a webhook or GitHub Actions build runs from the push, the agent should observe the build result and verify the app afterward. -## Change later -Delivery choice is not permanent. You can move from direct deploy to git-push later, add a build integration later, or switch to external handoff for a release that needs human control. -## Gotchas -1. **Delivery choice is not a hidden deploy button.** It records the future path after the app already works. -2. **External handoff is not a deploy by itself.** It means a human or external system owns delivery. -3. **GitHub Actions uses a Zerops token.** The Actions secret for `zcli` deploys is `ZEROPS_TOKEN`, not your GitHub token. +- the runtime service it changed, +- the deploy or verification target, +- the URL, endpoint, UI state, job result, or stored data that proves the requested behavior, +- managed services, env vars, or delivery settings it touched, +- the delivery preference that now applies. +If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the decision or credential needed from you. +Advanced reuse path: [Package a running service](/zcp/workflows/package-running-service) turns a verified runtime into a re-importable bundle. It is not the normal lifecycle for the next app change. +## Next steps ---------------------------------------- # Zcp > Workflows > Package Running Service -Use packaging when a deployed runtime should become a reusable import bundle. This is an advanced handoff or reuse workflow, not the normal way to deploy the next change. 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 layout into a fresh Zerops project. +Use packaging when a deployed runtime should become a reusable import bundle. This is an advanced handoff or reuse workflow, not the normal way to deploy the next app change. +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. The import file points back to the same source repo through `buildFromGit:`, so a fresh Zerops project can rebuild the app from git instead of carrying source code inside YAML. ## 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 a re-importable snapshot you can paste into a fresh project later. - 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 the next app change deployed. For that, ask for the app result; direct deploy is the expected ZCP path behind the request. - You want to deploy the next change to an existing production project. Keep production in its own Zerops project and operate it through Zerops UI, the API, or CI. -Packaging does not keep a long-running session. If interrupted, the agent reads live state and re-runs the export flow; you only decide when the dev or stage runtime, or env classification, is ambiguous. +Packaging does not keep a long-running session. If interrupted, the agent reads live state and prepares the bundle again. You only need to decide when the runtime choice or env-var classification is ambiguous. ## 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. +Packaging captures **one** runtime. If your project has a dev runtime and a separate stage runtime, the agent asks which one to package. They may carry different env values, start commands, or `setup:` blocks. For single-runtime projects, the choice is implied. +Managed services (`db`, `redis`, etc.) come along automatically as dependencies; you do not pick them one by one. 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. +The agent handles the packaging work; you only step in if it asks which runtime to package or how to classify a value. ## Classify env vars (secrets, project-scoped values, public values) -Once the runtime is chosen, 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. | -| `plain-config` | Literal runtime config (`LOG_LEVEL`, `NODE_ENV`, feature flags) | The literal value, verbatim. | -ZCP does not guess these buckets from key names alone. The agent should classify them from source code: if the value is read by a Stripe SDK call it is `external-secret`; if it appears as `${db_*}` from a managed service it is `infrastructure`. The packaging response carries env *keys* and redacts values; the agent fetches values only when it needs them. +Once the runtime is chosen, ZCP prepares 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. | +| `plain-config` | Literal runtime config (`LOG_LEVEL`, `NODE_ENV`, feature flags) | The literal value, verbatim. | +ZCP does not guess these buckets from key names alone. The agent should classify them from source code: if the value is read by a Stripe SDK call it is `external-secret`; if it appears as `${db_*}` from a managed service it is `infrastructure`. The packaging response carries env _keys_ and redacts values; the agent fetches values only 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. +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 #zeropsPreprocessor=on -# zerops-project-import.yaml — paste into a fresh Zerops project +# zerops-project-import.yaml - paste into a fresh Zerops project project: name: demo envVariables: @@ -26793,7 +27182,7 @@ services: priority: 10 ``` ```yaml -# zerops.yaml — verbatim copy from the running runtime +# zerops.yaml - verbatim copy from the running runtime zerops: - setup: appdev build: @@ -26815,26 +27204,26 @@ zerops: DATABASE_URL: postgres://${db_user}:${db_password}@${db_hostname}:${db_port}/${db_dbName} ``` A few things worth noticing: -- The first line is the [Zerops preprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `` directives (here `)>` for `APP_KEY`), `#zeropsPreprocessor=on` must be 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. +- The first line is the [Zerops preprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `` directives (here `)>` for `APP_KEY`), `#zeropsPreprocessor=on` must be line 1. Without it, the platform skips directive expansion at re-import and the literal string lands in the env var. ZCP adds this 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). -Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen handoff path expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through the normal [handoff path](/zcp/workflows/delivery-handoff). +Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen delivery preference expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through the normal [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). ## 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 | -| Production config | Keep production in a separate Zerops project | -## Gotchas -1. **Packaging is not the delivery path by itself.** It produces the bundle. Commit and push happen through your chosen [handoff path](/zcp/workflows/delivery-handoff). +| 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 | +| Production config | Keep production in a separate Zerops project | +## Packaging checks +1. **Packaging is not the delivery path by itself.** It produces the bundle. Commit and push happen through your chosen [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). 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. -- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) — the verified runtime this packages. -- [How ZCP works](/zcp/concept/how-it-works) — why packaging stays stateless. +- [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) - choose how the bundle's two files ship. +- [How ZCP works](/zcp/concept/how-it-works#verification-has-two-layers) - the verified runtime this packages. +- [How ZCP works](/zcp/concept/how-it-works) - why packaging stays stateless. ---------------------------------------- diff --git a/apps/docs/static/llms-small.txt b/apps/docs/static/llms-small.txt index b62097d7e..2aec8fc38 100644 --- a/apps/docs/static/llms-small.txt +++ b/apps/docs/static/llms-small.txt @@ -6169,54 +6169,63 @@ ZCP runs with a project-scoped token that grants operational rights inside that ::: ## Who it's for Developers using a coding agent on application work that needs real infrastructure. The developer stays focused on the product outcome, technology choices, acceptance criteria, and judgment. ZCP gives the agent the platform context and operations needed to use or create services, connect the app to infrastructure, deploy, verify, recover from failure, and report back. -The **Include Coding Agent** option bundles the currently supported agent CLI shown in the dashboard. Local setup can connect a compatible local agent client after `zcp init`. +The **Include Coding Agent** option currently bundles Claude Code and wires it to ZCP. The agent account remains yours: authenticate it with your own subscription login or API credentials. Additional bundled agents are planned. Local setup can connect a compatible local agent client after `zcp init`. ## What ZCP lets the agent do ZCP exposes a fixed set of operations on one Zerops project, grouped by user job. The prompt can stay focused on the app because the agent does not need you to describe what the project looks like - ZCP reports it from what is 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 | [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) | -| **Verify** | Reachability + behavior checks against the actual URL | [Deploy, verify, and fix](/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) | -| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | -| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | +| 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 with ZCP](/zcp/workflows/build-with-zcp#develop-with-live-project-context) | +| **Verify** | Reachability + behavior checks against the actual URL | [How ZCP works](/zcp/concept/how-it-works#verification-has-two-layers) | +| **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) | +| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | +| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | Behind one app request, the agent should read the project, use existing services or create missing ones, edit and deploy the app, verify the requested behavior, and then record how future changes should ship. A green deploy with a 500 on the relevant route is not done — verification has two layers and both must pass. Full workflow terms: [Workflow terms](/zcp/reference/agent-workflow). ## Two ways to run it The `zcp` binary can run in a Zerops service or on your laptop. The user choice is remote setup or local setup. - A Zerops service (`zcp@1`) inside the project runs ZCP. Enable **Include Coding Agent** to add the bundled agent CLI and preconfigure it to use ZCP. Enable Cloud IDE when you want browser VS Code. **Recommended starting point.** [Set up →](/zcp/setup/hosted-workspace) - The `zcp` binary on your laptop, initialized in a project folder with `zcp init`. The agent runs in your local editor or CLI; private service access uses `zcli vpn up`. [Set up →](/zcp/setup/local-agent-bridge) +**Remote setup** runs ZCP inside a Zerops `zcp@1` service. Enable **Include Coding Agent** for the bundled agent CLI, and **Cloud IDE** for Browser VS Code. This is the recommended starting point when broad agent permissions can stay inside the project. [Use remote workspace](/zcp/setup/hosted-workspace) +**Local setup** runs the `zcp` binary on your laptop after `zcp init`. The agent runs in your local editor or CLI; private service access uses `zcli vpn up`. Use it when local files, tools, data, or a local-only agent client should stay in charge. [Set up local ZCP](/zcp/setup/local-agent-bridge) Either way, the agent gets the same project-scoped ZCP operations against the same project. Remote setup can package ZCP with an agent CLI and browser editor; local setup connects your own agent client to ZCP. -Decision: [Choose remote or local setup](/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). +Decision: [Remote or local ZCP setup](/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 remote setup adds -Remote setup starts with a `zcp@1` Zerops service. Two dashboard options decide what is bundled into that service: -**Include Coding Agent.** Adds the currently supported bundled agent CLI and preconfigures it to use ZCP running inside the `zcp@1` service. Agent authentication is handled separately by that agent's login method. -**Cloud IDE.** Browser-based VS Code (code-server) with 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 → Cloud IDE](/features/local-remote-development#cloud-ide-on-remote-setup). -Without **Include Coding Agent**, the service can still exist, but there is no preconfigured agent waiting inside it. Without **Cloud IDE**, there is no browser editor. With both enabled, you get a one-click agent + IDE setup inside the project. -Both options are configured when you provision the service: [Set up remote ZCP → Path B](/zcp/setup/hosted-workspace#path-b--toggle-the-zcp-service). +Remote setup starts with a `zcp@1` Zerops service. **Include Coding Agent** adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use ZCP. **Cloud IDE** adds Browser VS Code so you can supervise or take over from the dashboard. +Without **Include Coding Agent**, there is no preconfigured agent waiting inside the service. Without **Cloud IDE**, there is no browser editor. Configure both when you provision the service if you want the first-run agent + IDE path. Details: [Use remote workspace](/zcp/setup/hosted-workspace#what-you-get). +Remote setup is still your workspace. You can install another agent CLI, add private MCP servers or helper processes, edit `CLAUDE.md` or other agent instruction files, and keep team tooling in the service. ZCP supplies the project-scoped control plane, operations, and workflow evidence; it does not lock the service to one agent or one set of tools. ## 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. +- Resource allocation and scaling limits are queryable. When a change affects cost, the agent can surface the proposed resource change and ask 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. ZCP is the ergonomic layer on top of Zerops APIs, `zcli`, and SSH for agents. It tightens the loop by packaging project state, guidance, operations, and verification behind one project-scoped interface. ## Operations and permissions -ZCP groups its operations into three categories so an agent client or team policy can gate them: - Inspect state, logs, events, configuration, and verification output. Safe to auto-allow. [Full list →](/zcp/reference/mcp-operations#read-only-operations) - Deploy, change env vars, manage lifecycle, scale, or delete services. Require team policy and confirmation where needed. [Full list →](/zcp/reference/mcp-operations#mutating-operations) - Set up infrastructure, mounts, imports, work sessions, or delivery configuration. Gated by team policy. [Full list →](/zcp/reference/mcp-operations#operational-setup) +ZCP groups its operations so an agent client or team policy can gate them: +- **Read-only operations** inspect state, logs, events, configuration, and verification output. [Full list](/zcp/reference/mcp-operations#read-only-operations) +- **Mutating operations** deploy, change env vars, manage lifecycle, scale, or change public access. They are governed by token permissions and your agent/team policy. [Full list](/zcp/reference/mcp-operations#mutating-operations) +- **Destructive operations** have extra ZCP gates: service deletion needs explicit same-conversation approval naming the service, and destructive replacement after failed deploy history requires evidence review first. [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) +- **Operational setup** covers infrastructure, mounts, imports, work sessions, and delivery configuration. [Full list](/zcp/reference/mcp-operations#operational-setup) That's the surface. The boundary is the project. The token authorizes one project, full stop — cross-project, account-wide, or organization actions stay outside reach. Integration tokens are still bounded by the permissions of the user who created them and by the project access selected for the token; see [Roles & Permissions](/features/rbac). Full blast-radius framing: [Trust model](/zcp/security/trust-model). Full operation list and permission semantics: [Advanced operations](/zcp/reference/mcp-operations). Token shape and confirmation gates: [Tokens and credentials](/zcp/security/tokens-and-project-access). ## Customize remote setup -Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product - install packages, run additional processes alongside the agent, or ship a hardened version with team-standard tools baked in. Patterns: [Customize remote setup](/zcp/setup/hosted-workspace#customize-remote-setup). -Local setup isn't extensible the same way; the binary on your laptop is just ZCP - editor, shell, and dev server stay your tools' job. +Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product. Install trusted tools, edit agent instructions, configure additional MCP servers, or ship a hardened version with team-standard tools baked in. Patterns and guardrails: [Make customization persistent](/zcp/setup/hosted-workspace#make-customization-persistent). +Local setup isn't extensible the same way; the binary on your laptop is just ZCP - editor, shell, local files, local data, and tool feedback stay on your machine. ## Source control -When the agent runs in remote setup, 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. In local setup, 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 remote setup, git access belongs inside the workspace: GitHub CLI login, a fine-grained git token stored on the `zcp` service, or SSH agent forwarding from a remote editor. In local setup, it uses your existing local git credentials. Detail: [Tokens and credentials -> Git and CI credentials](/zcp/security/tokens-and-project-access#git-and-ci-credentials). ## What ZCP is not ZCP is not a prompt-to-prototype generator, an isolated code sandbox, or an editor-only cloud development environment. It is the control plane that gives a coding agent project-scoped infrastructure operations on Zerops: services, env vars, deploys, logs, verification, recovery, and delivery handoff. +## How it differs from adjacent tools +ZCP is closest to the tools that let an agent work on real software over time, but the center of gravity is different: the agent operates one real Zerops project rather than a preview sandbox or editor-only workspace. +| Category | What it optimizes for | Where ZCP differs | +| ------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| Prompt-to-prototype tools | Fast artifact generation and visual preview. | ZCP targets a running app on real services, with deploy, logs, persistence, and verification. | +| Code execution sandboxes | Safe isolated execution. | ZCP gives the agent managed services, private networking, deployment, and project state that persist across sessions. | +| AI-enabled cloud IDEs | Editing code in a hosted development environment. | ZCP gives the agent platform operations too: create/use services, wire env vars, deploy, verify, recover, and hand off. | +| Primitive/API collections | Calling many specialized APIs. | ZCP keeps the app and dependencies in one Zerops project, reachable by service names and standard platform mechanics. | +| Primitive/API collections | Calling many specialized APIs. | ZCP keeps the app and dependencies in one Zerops project, reachable by service names and standard platform mechanics. | +The claim is not that ZCP replaces those tools. It solves the missing operational layer when the output must become a verified app running on Zerops infrastructure. ## Where to start ---------------------------------------- @@ -6710,15 +6719,15 @@ Containers are the most granular level of the Zerops architecture. Each service 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. Across local VPN, Cloud IDE, and SSH, you use the same project network, hostnames, service types, and deploy pipeline. This is also what narrows the gap between development and production. Development and staging can use the same Zerops-managed service types, networking model, and `zerops.yaml` patterns production uses. Production should still live in a separate project with separate credentials, access rules, and resource policies. :::note -Remote setup is a `zcp@1` service you can add to any project. It can provide a Linux dev container with a curated toolchain, optional Cloud IDE, and optional **Include Coding Agent** wiring. The modes below can use that service, connect directly to runtime services, or stay fully local over VPN; local development with `zcli vpn up` does not require ZCP at all. +Remote setup is a `zcp@1` service you can add to a development or staging project. It can provide a Linux dev container, optional Cloud IDE, and optional **Include Coding Agent** wiring. The paths below can use that service, connect directly to runtime services, or stay fully local over VPN; local development with `zcli vpn up` does not 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. +The development question is just: how do you, the developer, get onto that network? Three answers give you the same project-private network reach, with different filesystem, tooling, and credential boundaries. - **Local + VPN.** Your laptop joins the private network via WireGuard. Editor, toolchain, and processes stay where they are. Remote setup isn't required. - **Cloud IDE.** 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 the `zcp` service, or directly to a service container. -Same `db:5432`, same `api:3000`, same project credentials in all three. Switch modes without changing anything about the development project. +Same `db:5432`, same `api:3000`, same project credentials in all three. Switch paths without changing anything about the development project. ## Local + VPN **Best for:** keeping your existing editor, dotfiles, and toolchain — and offloading just the dependencies that are painful to run locally. ```bash @@ -6729,25 +6738,19 @@ 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: add remote setup, 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 service type as stage or production. -This mode is also where coding agents running on your laptop fit. Without ZCP, a local agent client inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. To let the agent operate the project - discover, deploy, verify, read logs, and manage delivery handoff - set up [local ZCP](/zcp/setup/local-agent-bridge). +This path is also where coding agents running on your laptop fit. Without ZCP, a local agent client inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. To let the agent operate the project - discover, deploy, verify, read logs, and manage delivery handoff - set up [local ZCP](/zcp/setup/local-agent-bridge). **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). ::: ## Cloud IDE on remote setup **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 remote setup (a `zcp@1` service) to a project — see [Set up remote ZCP](/zcp/setup/hosted-workspace) — you get an Ubuntu-based dev container with `zcp`, `zcli`, GitHub CLI, database CLIs, shell utilities, browser automation tools, and SSHFS. Enable **Cloud IDE** for browser-based VS Code; enable **Include Coding Agent** for the bundled agent CLI plus ZCP wiring. -Remote setup can also mount 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 Zerops deploy pipeline. One remote setup service can mount multiple dev services at once, covering the whole stack. +When you add remote setup (a `zcp@1` service) to a development or staging project - see [Use remote workspace](/zcp/setup/hosted-workspace) - you get a project-contained workspace with `zcp`, private-network access, optional Browser VS Code, and optional bundled agent wiring. +Remote setup can also mount runtime service files into the workspace, so you can inspect or edit code that runs in another container without moving it to your laptop. Details live in [Runtime file access](/zcp/setup/hosted-workspace#runtime-file-access). 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.** Remote ZCP isn't on your laptop, so your local SSH keys don't follow it. Two patterns work: +**Source control.** The remote 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 the `zcp` service's secret environment variables; `git` reads it through a credential helper. Either pattern keeps your token in the `zcp` service, not in your project's source. @@ -6759,7 +6762,7 @@ ssh apidev # any service in the project ssh frontenddev ssh zcp # if you provisioned the `zcp` service ``` -In this mode, remote setup 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. Remote setup becomes a *convenience* layer: a single container that carries the toolchain, mounts multiple dev services over SSHFS, and can include `zcli`, Cloud IDE, and coding-agent wiring for the project. If you'd rather connect your editor directly to your runtime services and skip remote setup entirely, that works. +In this path, remote setup 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. Remote setup becomes a convenience layer: a single workspace that can carry tools, runtime file mounts, Cloud IDE, and coding-agent wiring for the project. If you'd rather connect your editor directly to your runtime services and skip remote setup 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 the `zcp` service) uses your laptop's GitHub key over the forwarded agent socket — no token storage required. ```ssh-config # Local ~/.ssh/config @@ -6770,34 +6773,34 @@ Host apidev.zerops :::info SSH access details See the [SSH reference guide](/references/networking/ssh). ::: -## Picking a mode -| | Local + VPN | Cloud IDE | Native IDE over SSH | -|---|---|---|---| -| Editor runs | Local | Browser | Local | -| Toolchain | Local | Remote ZCP | Remote ZCP or service container | -| Dev databases | Hostname via VPN | Native, on private network | Native, on private network | -| Remote ZCP 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 | -| 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 remote setup are hitting the same `db:5432`. -For the agent-driven path, the setup decision (remote `zcp@1` service vs. local `zcp` binary) is on its own page: [Choose remote or local setup](/zcp/setup/choose-workspace). +## Picking a path +| | Local + VPN | Cloud IDE | Native IDE over SSH | +| --------------------- | ------------------ | -------------------------- | --------------------------------- | +| Editor runs | Local | Browser | Local | +| Toolchain | Local | Remote setup | Remote setup or service container | +| Dev databases | Hostname via VPN | Native, on private network | Native, on private network | +| Remote setup 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 | +| Best for | Keeping your setup | Disposable environments | Native UX, lighter laptop | +You're not locked into one path. The same project can support all three without reprovisioning when your source-of-truth handoff is clear. A teammate on Local + VPN and an agent inside remote setup are hitting the same `db:5432`. +For the agent-driven path, the setup decision (remote `zcp@1` service vs. local `zcp` binary) is on its own page: [Remote or local ZCP setup](/zcp/setup/choose-workspace). ## Same architecture, separate production -The development environments above don't approximate production by inventing a different platform. A ZCP development project can run dev and stage services with the same Zerops-managed service types, private networking, load-balancer model, and `zerops.yaml` patterns that production uses in its separate project. Dev, stage, and production differ in resource allocation, access rules, and credentials, not in architecture. +The development environments above don't approximate production by inventing a different platform. A Zerops development/staging project can run dev and stage services with the same Zerops-managed service types, private networking, load-balancer model, and `zerops.yaml` patterns that production uses in its separate project. Dev, stage, and production differ in resource allocation, access rules, and credentials, 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 and stage, you've already tested it against the kind of infrastructure it'll meet in production. The remaining differences are scale, credentials, access policy, and data. This applies whether the developer is human or an agent — see [ZCP for Coding Agents](/features/coding-agents) for the agent case. ## How this differs from cloud IDEs If you've used GitHub Codespaces or Gitpod, Cloud IDE 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 use the same platform model.** 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 model: managed Postgres, private networking, and `zerops.yaml`. Production still lives in its own project with its own credentials and policies. -Remote ZCP 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. +Remote setup 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 - 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 → [ZCP for Coding Agents](/features/coding-agents) -- Set up remote setup → [Set up remote ZCP](/zcp/setup/hosted-workspace) -- Pick remote or local ZCP → [Choose remote or local setup](/zcp/setup/choose-workspace) +- Use remote workspace → [Use remote workspace](/zcp/setup/hosted-workspace) +- Remote or local ZCP setup → [Remote or local ZCP setup](/zcp/setup/choose-workspace) - Term reference → [ZCP Glossary](/zcp/glossary) ---------------------------------------- @@ -21516,284 +21519,492 @@ For advanced configurations or custom requirements: # Zcp > Concept > How It Works -ZCP turns product or app intent into a verified deployed app by giving the coding agent two things it normally lacks: current knowledge of the Zerops project and the tools to change, deploy, inspect, and verify it. -## The model in one pass -Product intent enters a project loop: read current state, decide whether the needed services already exist, change the app and its Zerops wiring, deploy, verify from evidence, and return either proof or a concrete blocker. +ZCP gives a coding agent an operating loop for one Zerops project. The agent starts from live project state, chooses the app runtime and dependencies, makes the application change, deploys through Zerops, verifies real behavior, and then returns proof or a concrete blocker. +The loop is carried by three things: project-scoped tools, Zerops-specific knowledge, and workflow instructions that tell the agent what to inspect, what it can change, when it should ask, and what counts as done. The point is not that you run a workflow by hand. The point is that the agent has a project-aware path behind a product request and can make the important decisions visible while it works. +## The control loop ```mermaid flowchart TD intent["Product intent -Build a task board where tasks stay saved after refresh."] - context["ZCP context -live services, env vars, logs, events, -runtime guidance, available operations"] - target["Project fit -use existing services or create missing ones; -select the app runtime, -not the zcp setup service"] - app["Build the app -code, zerops.yaml, env wiring"] - deploy["Deploy through Zerops"] - verify{"Verify real behavior -endpoint or UI"} - fix["Read evidence -logs, events, checks"] - done["Done -URL, endpoint, or UI proof"] +Build a task board for my team."] + state["Live project state +services, runtime layout, env vars, +logs, events, saved work state"] + scope["Runtime scope +which app service changes"] + setup{"Missing or unsuitable +services?"} + provision["Service setup +use existing services or create +missing runtimes/dependencies"] + appwork["App work +code, zerops.yaml, env refs, +migrations, seeds, framework config"] + deploy["Direct deploy through Zerops"] + reachability{"Runtime reachable? +status, logs, HTTP probe"} + behavior{"Requested behavior works? +endpoint, UI flow, worker result, +persisted state"} + evidence["Evidence +build logs, runtime logs, +events, verify output"] + proof["Proof +URL, endpoint result, +UI state, or stored result"] + delivery["Delivery after proof +keep direct deploy, push to git, +or hand off"] blocker["Blocker -missing credential, decision, -or repeated failure"] - delivery["Future delivery -direct deploy, git push, or CI handoff"] - intent --> context --> target - target --> app --> deploy --> verify - verify -->|fixable| fix --> app - verify -->|verified| done --> delivery - verify -->|needs human| blocker +credential, decision, +unsupported fit, repeated failure"] + intent --> state --> scope --> setup + setup -->|yes| provision --> appwork + setup -->|no| appwork + appwork --> deploy --> reachability + reachability -->|no, fixable| evidence --> appwork + reachability -->|yes| behavior + behavior -->|no, fixable| evidence + behavior -->|yes| proof --> delivery + setup -->|needs decision| blocker + reachability -->|needs human| blocker + behavior -->|needs human| blocker classDef user fill:#f7fbff,stroke:#2d72d9,stroke-width:1.5px,color:#172033; classDef zcpbox fill:#eef7f1,stroke:#32845a,stroke-width:1.5px,color:#172033; classDef work fill:#fffaf0,stroke:#c47f17,stroke-width:1.5px,color:#172033; classDef done fill:#f5f3ff,stroke:#7157d9,stroke-width:1.5px,color:#172033; classDef stop fill:#fff1f2,stroke:#d33f49,stroke-width:1.5px,color:#172033; class intent user; - class context zcpbox; - class target,app,deploy,verify,fix work; - class done,delivery done; + class state,scope zcpbox; + class setup,provision,appwork,deploy,reachability,behavior,evidence work; + class proof,delivery done; class blocker stop; ``` -The important signals are simple: the agent read current project state, named the target runtime, verified the requested behavior, and explained what remains. -## Where ZCP runs -ZCP can run in two places: -| Path | What runs where | What changes for you | -|---|---|---| -| Remote setup | A `zcp@1` service runs the `zcp` binary inside the Zerops project. **Include Coding Agent** adds the bundled agent CLI and preconfigures it to use ZCP; **Cloud IDE** adds browser VS Code. | The work stays inside the project. The agent can use project-private networking and SSHFS mounts for runtime files. | -| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your editor's agent talks to it. | Your checkout and dev server stay local. Managed services are reached over VPN, and `.env` generation bridges project credentials into your local app. | -| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your editor's agent talks to it. | Your checkout and dev server stay local. Managed services are reached over VPN, and `.env` generation bridges project credentials into your local app. | -The project-scoped control plane is the same idea in both paths. The filesystem, network path, deploy source, and safety profile are different. See [Choose remote or local setup](/zcp/setup/choose-workspace). -## What ZCP reads -ZCP reads live project state instead of relying on a long prompt: -- services and whether they are runtime or managed dependencies, -- runtime layout, such as one app runtime, a dev+stage pair, or a local checkout linked to a Zerops runtime, -- service env-var keys and references, -- build/deploy events, runtime logs, and verification results, -- the current work state when a session is interrupted. -Recovery starts from live state. If a session gets confused or interrupted, ask the agent to read ZCP status before changing anything else. -## What counts as done -A task is not done when code is written, or when a build succeeds. A ZCP task is done when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the URL or a concrete blocker. -Workflow path: [Build with ZCP](/zcp/workflows/build-with-zcp). Exact terms and runtime layouts: [Workflow terms](/zcp/reference/agent-workflow). +The loop gives the agent a way to resolve each phase from project evidence instead of from a guessed checklist. It knows where to read current state, which operations are scoped to the project, which Zerops rules apply to app wiring, which evidence to read after deploy, and when the result is proof versus a blocker. +## What "project state" means +ZCP reads the project instead of asking you to paste a service inventory into the prompt. Useful state includes: +- runtime services and managed services, +- whether the app has one runtime, a dev+stage pair, or local files linked to a Zerops runtime, +- env-var keys and Zerops references, +- recent build/deploy events, +- build logs, runtime logs, and verification output, +- saved work state after an interrupted session. +This is why a short prompt can be enough. The agent can ask ZCP what exists, which runtime was last deployed, which checks passed, and where a previous run stopped. +Chat history is not the source of truth. If the agent sounds confused, starts from an old assumption, or a session was interrupted, the recovery move is: +```text +Read ZCP status and tell me where this project stands before changing anything. +``` +## What ZCP coordinates behind the prompt +ZCP is intentionally opinionated about the concerns an agent has to resolve during app work. You usually do not name these concerns in the prompt. ZCP provides them through project state, tools, guidance, and workflow instructions so the agent can work from evidence. +| Concern | What ZCP gives the agent | What that means for you | +| ---------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| Project state | Live services, env-var keys, Zerops references, recent events, logs, verification output, and saved work state. | You do not have to paste a service inventory or reconstruct what happened. | +| Runtime target | A model for choosing the app runtime that should receive code changes. | The agent can identify where app work belongs before editing or deploying. | +| Managed services | Knowledge and env wiring patterns for database, cache, queue, search, storage, mail, and similar services. | Product intent can imply real dependencies without a manual wiring checklist. | +| Service setup | Project-scoped operations for using existing services or creating missing runtimes and dependencies. | Runtime layout and dependencies can be established before app work starts. | +| App wiring | Zerops-specific rules for `zerops.yaml`, env references, ports, commands, public access, and build/deploy behavior. | The app is wired for Zerops rather than for a generic cloud template. | +| Deploy evidence | Build logs, runtime logs, platform events, service status, HTTP checks, and structured verification output. | A failed deploy becomes a diagnosis surface instead of guesswork. | +| Behavior proof | A done gate based on the requested behavior, not only on a successful build or reachable root URL. | The final answer can point to what was actually checked. | +| Delivery handoff | A workflow contract for direct proof first, then git push, CI, or human handoff when that is the chosen path. | Shipping setup follows a verified running result. | +| Delivery handoff | A workflow contract for direct proof first, then git push, CI, or human handoff when that is the chosen path. | Shipping setup follows a verified running result. | +The exact labels for layouts, delivery modes, and setup routes live in the [Glossary](/zcp/glossary) and [Workflow terms](/zcp/reference/agent-workflow). +## Service setup prepares the project layout +Before app code work starts, ZCP helps the agent make three decisions visible: +- Which runtime service is the app target? +- Which managed services are dependencies? +- Does the existing project layout fit the request? +If the services already exist, the agent can use them. If a needed runtime or dependency is missing, ZCP can create it through project-scoped operations. If the choice affects cost, product scope, credentials, a destructive action, or which stage/runtime should be used, the workflow tells the agent to stop and ask. +Service setup is finished when the app runtime and dependencies are known. It is not the finished app; it is the project layout that lets app work happen in the right place. +## App work changes code and platform wiring together +After the runtime scope is known, ZCP gives the agent the platform knowledge needed to make the application change. In practice this often spans both source code and Zerops wiring: +- app files, +- `zerops.yaml` build and run setup, +- env references to managed services, +- migrations, seeds, and framework config, +- start commands, ports, and public HTTP support, +- local `.env` generation when using local setup. +ZCP guidance matters because Zerops is its own platform. Service references, build/deploy behavior, public access, scaling, and env resolution do not follow Docker Compose or Kubernetes conventions. The agent gets Zerops-specific rules instead of relying on a generic cloud template. +The first functional deploy goes through ZCP's direct deploy path. That deploy creates the running result the agent can verify. A repository push or CI handoff can follow, but it should not replace the first proof. +## Recovery is evidence-driven +When something fails, ZCP gives the agent the evidence surface that matches the failure: +| Failure pattern | Useful evidence | +| ------------------------ | ------------------------------------------------------------------------------------------------------------ | +| Build failed | Build logs, build commands, dependency manifests, deploy file list. | +| Runtime failed to start | Runtime or prepare logs, start command, ports, env references. | +| Route or behavior failed | Verify output, HTTP response, runtime logs at request time, stored state. | +| Network path failed | VPN, SSH, DNS, subdomain readiness, service status, named transport error. | +| Config was rejected | Field-level rejection, `zerops.yaml`, setup name, env reference, service settings. | +| Credential failed | The named credential surface: Zerops token, git token, SSH, managed-service credential, or external API key. | +| Credential failed | The named credential surface: Zerops token, git token, SSH, managed-service credential, or external API key. | +Retrying the same deploy without new evidence is not progress. The workflow pushes the agent toward one of three outcomes: fix from a cause, ask for the missing decision, or report a blocker with the category, evidence read, and attempts made. +## Verification has two layers +A successful deploy proves that Zerops accepted the build and started the runtime. Platform reachability checks prove the service is running and reachable. They still do not prove the product request. +For a task-board app, useful behavior proof might be: +- create a task, +- move it between columns, +- refresh the page, +- confirm the task is still there. +For an API task, proof might be a JSON response from the requested endpoint and stored data behind it. For a worker task, proof might be a processed job and the resulting database or object-storage state. For a staging request, proof belongs on the stage runtime, not only on dev. +The final answer should make that proof inspectable: runtime name, URL or endpoint, behavior checked, and delivery choice if one was set. +## Delivery happens after proof +Delivery choice controls how future changes ship after a verified runtime exists: +| Choice | Use when | +| ------------------ | ------------------------------------------------------------------------------ | +| Keep direct deploy | Early development, demos, dev/stage iteration, or agent-owned runtime changes. | +| Push to git | You want commits, review, repository history, or a repo-triggered build. | +| External handoff | CI, release management, or a human owns the next deploy. | +| External handoff | CI, release management, or a human owns the next deploy. | +Packaging a running service is a separate advanced reuse path. It turns a deployed runtime into a re-importable bundle for another Zerops project. It is useful for handoff or reuse, not for deploying the next app change; see [Package a running service](/zcp/workflows/package-running-service). +## Remote and local setup use the same loop +The same `zcp` binary can run in two places. The control loop stays the same; the filesystem and network path change. +| Path | What runs where | Practical effect | +| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Remote setup | A `zcp@1` service runs the `zcp` binary inside the Zerops project. **Include Coding Agent** adds the bundled agent CLI; **Cloud IDE** adds browser VS Code. | Work happens inside the project boundary. The agent can use project-private networking and runtime file mounts. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your local editor or CLI agent talks to it. | App files, local data, deploy source, and git credentials stay on your machine. Managed services are reached over Zerops VPN, and `.env` generation bridges project credentials into your local app. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your local editor or CLI agent talks to it. | App files, local data, deploy source, and git credentials stay on your machine. Managed services are reached over Zerops VPN, and `.env` generation bridges project credentials into your local app. | +Base the setup path on where you want the agent and filesystem to live: [Remote or local ZCP setup](/zcp/setup/choose-workspace). +## Signs of a healthy ZCP run +A well-shaped run should: +- name the runtime scope before editing or deploying, +- use existing services when they fit, +- create missing services only before app work starts, +- read logs, events, and verify output when failure occurs, +- distinguish runtime reachability from requested behavior, +- stop before destructive actions, ambiguous runtime/stage choices, or missing credentials, +- end with proof or a concrete blocker. +That is the practical difference between "the agent wrote code" and "the app task is done". +## Where to go deeper +- [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. +- [Workflow terms](/zcp/reference/agent-workflow) - exact runtime layouts, delivery terms, failure categories, and completion evidence. +- [Troubleshooting](/zcp/reference/troubleshooting) - practical recovery when a run gets stuck. ---------------------------------------- # Zcp > Glossary +Use the exact labels here when you are reading ZCP status, writing agent policy, debugging a session, or reviewing a handoff. In normal prompts, describe the outcome you want. ## Core names **ZCP** - Zerops Control Plane for coding agents: the project-scoped control plane documented in this section. -**ZCP operations** - the project-scoped operations surface exposed to a coding agent. In MCP clients, this appears as the ZCP MCP server. -**`zcp` binary** - the executable. Runs inside remote setup or on your laptop in local setup. +**ZCP operations** - the project-scoped operations surface exposed to a coding agent. In MCP clients, this usually appears as the `zerops` server. +**MCP server** - the Model Context Protocol server that exposes ZCP operations to an agent client. +**Agent client** - the editor, CLI, or hosted agent runtime that connects to ZCP operations. +**`zcp` binary** - the executable. It runs inside remote setup or on your laptop in local setup. +**`zcp` service** - the service instance in a Zerops project that hosts remote setup. +**`zcp@1` service** - the Zerops service type behind remote setup. **zCLI** - the Zerops command-line client for humans, scripts, VPN, and CI. It coexists with ZCP. **zsc** - the in-container Zerops Setup Control utility used from `zerops.yaml`. ## Where ZCP runs **Remote setup** - ZCP running inside a Zerops `zcp@1` service in the project. -**`zcp@1` service** - the Zerops service type behind remote setup. -**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI and preconfigures it to use the project's ZCP operations. +**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use the project's ZCP operations. **Cloud IDE** - browser-based VS Code served by remote setup. +**Browser VS Code** - the dashboard entry point into the Cloud IDE. **AI Agent environment** - a recipe environment preset that creates app services plus remote setup with **Include Coding Agent** enabled. It is a recipe preset, not a separate ZCP setup path. **Local setup** - the `zcp` binary running on your laptop after `zcp init`, while your editor or CLI agent talks to it. **Local agent bridge** - local setup with the `zcp` binary connected to a local agent client. -## Workflows and results +**Env bridge** - local setup behavior that writes a local `.env` snapshot from project env vars and Zerops references so local app code can reach managed services over VPN. +## Work phases and loops **Service setup** - the infrastructure phase behind an app prompt. It uses existing runtime and managed services when they fit, creates missing services when needed, then stops before app code, `zerops.yaml`, or deploy. -**Deploy, verify, and fix** - the app phase. It scopes the runtime, changes code/config, deploys, verifies reachability, verifies requested behavior, and fixes failures. +**Infrastructure setup** - user-facing synonym for service setup. +**Setup route** - the exact label for how service setup starts: +- `adopt` - runtime services already exist and should be used. +- `recipe` - an empty or ZCP-only project matches a known recipe/stack. +- `classic` - an empty project needs a custom service plan. +- `resume` - an interrupted setup should continue from saved state and live services. +**Deploy, verify, and fix** - the app phase. It scopes the runtime, changes code/config, deploys, verifies reachability, verifies requested behavior, and fixes failures from evidence. +**Develop flow** - exact reference name for the deploy, verify, and fix loop. **Runtime scope** - the app runtime the change targets, such as `appdev`, `appstage`, `app`, or a linked local target. -**Delivery choice** - what happens to future changes after a verified deploy: direct deploy, git-push, or external handoff. +**Direct deploy** - a ZCP deploy from the current source to the scoped runtime. The first verified running result uses direct deploy before delivery setup is applied. +**Reachability verification** - checks that the runtime exists, is running, has no recent blocking errors, and can answer an HTTP probe when it is an HTTP service. +**Behavior verification** - checks that the user-requested app behavior works on the real URL, endpoint, worker result, or stored state. +**Completion evidence** - the runtime, deploy, reachability check, behavior check, URL/endpoint/UI/state proof, and delivery choice or blocker that justify calling the task done. +**Proof** - user-inspectable completion evidence, such as a URL, endpoint result, UI state, processed job, or stored result. **ZCP status** - a live project read the agent can use after interruption or confusion. Ask for it before letting the agent change anything else. **Blocker** - a clear stop state with evidence: failure category, runtime in scope, what was tried, and what decision or credential is needed. +## Delivery and reuse +**Delivery choice** - what happens to future changes after a verified deploy: direct deploy, git-push, or external handoff. +**Delivery mode** - exact reference label for delivery choice: +- `auto` - keep direct deploy for future changes. +- `git-push` - commit and push to a configured remote, then observe/verify any tracked build. +- `manual` - external CI, release process, or a human owns future delivery. +**Git-push capability** - whether ZCP has enough remote URL and credential setup to push from remote setup. It can exist even when the current delivery mode is `auto`. +**Build integration** - repository-triggered build/deploy path that ZCP may configure or observe, such as a Zerops dashboard webhook or GitHub Actions. It is separate from git-push capability and delivery mode. +**External handoff** - delivery mode where ZCP records proof and state, but a human or external system owns future deploys. +**Package a running service** - advanced reuse workflow that turns one deployed runtime and its managed dependencies into a re-importable bundle. It is not the normal path for deploying the next app change. +**Packaging env bucket** - exact labels used when classifying project env vars during packaging: +- `infrastructure` - value comes from a managed-service reference and should be regenerated by the new project. +- `auto-secret` - local app secret that should be freshly generated on re-import. +- `external-secret` - third-party API key or credential that becomes a `REPLACE_ME` placeholder. +- `plain-config` - literal non-secret config copied into the bundle. ## Project and runtime terms **Runtime service** - a service that runs app code. -**Managed service** - database, cache, queue, search, storage, or similar dependency. It provides connection details; it is not an app deploy target. +**Target runtime service** - the specific runtime service selected for the current app change. +**Managed service** - database, cache, queue, search, storage, mail, or similar dependency. It provides connection details; it is not an app deploy target. **Runtime layout** - which app runtime services ZCP should use: - `standard` - dev runtime plus explicit stage runtime. - `dev` - one mutable development runtime. - `simple` - one runtime with no dev/stage split. -- `local-stage` - local checkout linked to one Zerops runtime as deploy target. -- `local-only` - local checkout with no linked runtime yet. +- `local-stage` - local source directory linked to one Zerops runtime as deploy target. +- `local-only` - local source directory with no linked runtime yet. +**Dev runtime** - mutable runtime used for iterative development. +**Stage runtime** - review or promotion target. Stage is not production. **Service scaling mode** - Zerops scaling setting such as `HA` or `NON_HA`. Different from runtime layout. -**Stage** - review or promotion target. Stage is not production. +**Public subdomain access** - Zerops public URL access for an eligible HTTP runtime. Workers and non-HTTP services do not get useful public URLs. +## Failures and recovery +**Failure category** - exact label that points to the first useful evidence surface: +- `build` - build phase failed. +- `start` - build passed, but runtime start or prepare failed. +- `verify` - runtime exists, but reachability or behavior failed. +- `network` - transport, DNS, VPN, SSH, or service-to-service reach failed. +- `config` - `zerops.yaml`, env vars, setup block, or service settings mismatch. +- `credential` - Zerops, git, SSH, managed-service, or external API credential failed. +- `other` - no known category matched. +**Recovery hint** - structured next move surfaced by ZCP when the failure has an actionable recovery path. +**Confirmation gate** - an operation that pauses until the user explicitly confirms the named target or consequence. +**Destructive import override** - import action that would replace an existing service stack. ZCP refuses first, names the affected services, and requires a matching acknowledgment before proceeding. ## Credentials +**Project-scoped token** - Zerops token that can access exactly one project. ZCP expects this scope. **`ZCP_API_KEY`** - project-scoped Zerops token used by ZCP. **`GIT_TOKEN`** - git provider credential used by remote git-push delivery when needed. **`ZEROPS_TOKEN`** - Zerops token commonly used by GitHub Actions or external CI that runs `zcli`. +**External secret** - third-party credential such as Stripe, OpenAI, Mailgun, or GitHub API access. ZCP can wire placeholders and env vars, but the secret itself remains your responsibility. ---------------------------------------- # Zcp > Overview -ZCP is the Zerops control plane setup that lets a coding agent work from real project state instead of a hand-written operations checklist. It gives the agent current project knowledge, guarded project operations, deploy/verify evidence, and Zerops guidance for connecting an app to its infrastructure. -It can run remotely inside a `zcp@1` service with **Include Coding Agent** enabled, or locally as the `zcp` binary initialized beside your CLI/editor agent. In both cases, the developer stays focused on product intent, technology choices, acceptance criteria, and decisions that require human judgment. -## What ZCP does -ZCP makes four things available to the agent inside a normal Zerops project: -**Project awareness.** The agent can read services, runtime layout, env vars, logs, events, and current work state instead of asking you to describe the project. -**Infrastructure wiring.** The agent can separate the `zcp` setup service, app runtime services, and managed dependencies, then connect the app to the right env vars and service references. -**Deploy and verify loop.** The agent can prove the app works on the selected runtime, not stop after writing code or seeing a green build. -**Delivery choice.** After verified work, the agent can keep deploying directly, push to git, or use external handoff to your CI or a human. -You provide product intent and judgment: what to build, which runtime or stage matters when you care, and what result counts as done. -## A good ZCP session -A ZCP-backed session should be product-led and evidence-based: -- The agent reads the project before changing it. -- It uses existing services when they fit and creates missing infrastructure when they do not. -- It deploys and verifies the requested behavior on the real endpoint or UI. -- It ends with a URL or proof of behavior, or with a blocker that names the missing decision, credential, or repeated failure. -## What you no longer have to do -ZCP is valuable because it removes the operational checklist from the prompt. You should not have to: -- describe every service already in the project, -- decide which env-var references connect the app to managed services, -- copy database credentials, logs, or deploy timelines into chat, -- repeat deploy, verify, log-reading, and URL-reporting instructions on every app task, -- translate a failed build or broken route into a guess before the agent can act, -- choose internal workflow names before describing what you want built. -You still own the product intent, technology constraints, acceptance criteria, credentials that live outside Zerops, and approval for destructive actions. -## Where to start -| Goal | Page | -|---|---| -| Try ZCP once with no local install | [Quickstart](/zcp/quickstart) | -| Understand the model before setup | [How ZCP works](/zcp/concept/how-it-works) | -| Choose remote vs local | [Choose remote or local setup](/zcp/setup/choose-workspace) | -| Add remote setup | [Set up remote ZCP](/zcp/setup/hosted-workspace) | -| Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | -| Build or change an app | [Build with ZCP](/zcp/workflows/build-with-zcp) | -| Decide how finished work ships | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | -| Understand tokens and boundaries | [Trust model](/zcp/security/trust-model) | -| Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | -| Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | -| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | -| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | +ZCP is a project-scoped control plane for AI coding agents over one Zerops project. It gives the agent the current truth about that project, project-scoped tools for operating it, and rules for deciding what should happen next and when the work is done. +The practical effect is that the agent can work from the project's real state instead of a guessed checklist. It can identify the right app runtime and managed services, make Zerops changes through project-scoped operations, read evidence when something fails, and finish with proof or a concrete blocker. +That changes the job you give the agent. You provide the product intent, technical constraints, and quality bar. ZCP provides the platform reality: what exists, where code runs, which dependencies are available, what was deployed, what passed verification, and what the next evidence-backed step should be. +## What ZCP gives the agent +**Project truth.** Services, app runtime layout, managed dependencies, env-var keys and references, logs, events, deploy history, verification state, and current work state. +**Zerops control.** Project-scoped operations for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. +**Workflow.** ZCP combines two work loops: infrastructure for using existing Zerops services or creating missing ones, and development for code, `zerops.yaml`, deploy, verification, and delivery choice. The goal is to keep Zerops work aligned with platform and development best practices while staying behind the product task, not becoming another checklist. +**Evidence-based completion.** A build or deploy is not the finish line. A completed app task should end with a working URL, endpoint result, UI proof, or a blocker backed by logs, events, and verification evidence. +## What you no longer have to script +Without ZCP, an app prompt often turns into an operations runbook. With ZCP, you should not need to paste: +- the service map, runtime target, dev/stage state, or managed-service inventory, +- database credentials, private hostnames, env-var references, or generated connection strings, +- build logs, runtime logs, event timelines, or a guess about why the last deploy failed, +- a deploy/verify/recovery script for every task; ZCP treats proof or a concrete blocker as part of completion, +- a recap after the chat loses context; the agent can read current ZCP status. +**You still own the decisions that need human judgment:** product intent, technology constraints, acceptance criteria, external credentials, repository policy, and approval for destructive actions. +## Where ZCP runs +The **same `zcp` binary** runs in both setups. In [remote setup](/zcp/setup/hosted-workspace), Zerops packages it inside a `zcp@1` service in the project. In [local setup](/zcp/setup/local-agent-bridge), you install the binary on your machine and connect your own editor or CLI agent to the project. The mental model is the same: one project, live state, scoped operations, deploy/verify evidence. The filesystem, network path, deploy source, and safety profile differ. +To start using ZCP, choose one path: add remote setup to a Zerops project, or initialize local setup beside your editor or CLI agent. The [Quickstart](/zcp/quickstart) uses remote setup because it needs no local install. +## Your agent, credentials, and workspace +**Agent account.** Remote setup can include a bundled agent CLI, currently Claude Code, already configured to use ZCP. Zerops wires the agent to the project; it does not provide or own your model account. You authenticate the agent with your own subscription login or API credentials. +**Zerops token.** ZCP itself connects to Zerops through a project-scoped Zerops token. In remote setup, Zerops injects that token into the `zcp@1` service. In local setup, you provide a project-scoped token when you initialize ZCP beside your editor or CLI agent. Token details live in [Tokens and credentials](/zcp/security/tokens-and-project-access). +**Workspace freedom.** The `zcp@1` service is still a normal project service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and adapt the workspace around the bundled setup. ZCP is the project control plane available to the agent; it is not a closed agent product. Details live in [Use remote workspace](/zcp/setup/hosted-workspace#make-customization-persistent). +:::caution Production boundary +Use ZCP in development or staging projects. Production should stay in a separate Zerops project and receive promoted work through your CI or release process; see [Production boundary](/zcp/security/production-policy). +::: +## Start here +First-read path: +| If you want to | Read | +| ----------------------------------------- | -------------------------------------------------------- | +| Try the guided hands-on path | [Quickstart](/zcp/quickstart) | +| Understand what happens behind the prompt | [How ZCP works](/zcp/concept/how-it-works) | +| Compare remote and local setup | [Remote or local ZCP setup](/zcp/setup/choose-workspace) | +| Start building after setup | [Build with ZCP](/zcp/workflows/build-with-zcp) | +| Start building after setup | [Build with ZCP](/zcp/workflows/build-with-zcp) | +Specific tasks and reference: +| If you want to | Read | +| ---------------------------------------- | --------------------------------------------------------------------------- | +| Use remote workspace in a project | [Use remote workspace](/zcp/setup/hosted-workspace) | +| Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | +| Decide how finished work ships | [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | +| Understand tokens and project boundaries | [Trust model](/zcp/security/trust-model) | +| Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | +| Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | +| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | +| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | ## What stays outside ZCP -ZCP is a control plane for agents, not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, scaling, and service references remain canonical for platform behavior. -## Names that matter -- **ZCP** - Zerops Control Plane in its coding-agent setup. -- **Remote setup** - a `zcp@1` service inside the Zerops project. The service runs the `zcp` binary and exposes ZCP to the agent from inside the project. -- **Local setup** - the `zcp` binary installed on your machine and initialized in a project folder with `zcp init`. -- **Include Coding Agent** - the remote setup option that adds the bundled agent CLI and preconfigures it to use ZCP in the service. -- **Cloud IDE** - optional browser-based VS Code in remote setup. -- **`zcp` binary** - the executable. In remote setup it is inside the `zcp@1` service; in local setup you install it and run `zcp init` in a project folder. -For the full vocabulary, including `zcp`, zCLI, zsc, MCP, runtime layouts, and credential names, see the [Glossary](/zcp/glossary). +ZCP is not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, scaling, and service references remain canonical for platform behavior. ---------------------------------------- # Zcp > Quickstart -Remote setup is the fastest first run: no local `zcp` install, no local MCP config, and the bundled agent is already connected to the project. You will open a project with **Include Coding Agent** enabled, give the agent a small app request, and inspect what it verified. +Use this path when you want to see ZCP working with the least setup. It is intentionally one path: deploy a recipe that already includes the **AI Agent** environment, authorize Claude Code, open Browser VS Code, and ask for product work in natural language. +Zerops has a [recipes catalog](https://app.zerops.io/recipes) for many runtimes and frameworks. Some recipes include an **AI Agent** environment, which is the ready-to-go ZCP setup: app services, managed services, the `zcp@1` workspace service, Browser VS Code, and bundled agent wiring in one deploy. +This quickstart uses the [Laravel showcase recipe](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent) because it includes real managed services and the AI Agent environment. The same flow works for other recipes that offer AI Agent. ## Prerequisites - A Zerops account with permission to create a project. -- A login for the bundled agent shown in the dashboard. -## Create a remote setup project -The fastest path is a recipe with the **AI Agent** environment: +- A Claude Code subscription login or API authentication. Claude Code is the bundled agent in the current dashboard flow; support for additional bundled agents is planned. Zerops wires Claude Code to ZCP, but your agent subscription or model credentials stay yours. +## 1. Choose the AI Agent recipe 1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes). -2. Pick a recipe that matches the stack you want to try. -3. Select the **AI Agent** environment. -4. Deploy the recipe. -The **AI Agent** environment creates the app services plus a `zcp@1` remote setup with **Include Coding Agent** enabled. Keep **Cloud IDE** enabled too so you can open the browser workspace. If you start from an empty project instead, enable **Add Zerops Control Plane (ZCP) service** during project creation and keep **Include Coding Agent** on; the agent can use existing app services or create the missing ones from your prompt. Full setup path: [Set up remote ZCP](/zcp/setup/hosted-workspace). -## Open remote setup -When provisioning finishes, open the `zcp` service in the Zerops dashboard. If **Cloud IDE** is enabled, open its workspace URL; a browser editor opens with the agent connected to this project through ZCP. -Ask for what the app should do. Service setup, deploy, verification, and reporting are part of the ZCP-backed agent contract. -In the agent chat: +2. Open a recipe with an **AI Agent** environment. For example, open [Laravel showcase](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent). +3. Select **AI Agent**. +4. Keep **Coding Agent** and **Cloud IDE** enabled. +5. Deploy the recipe. +The deploy creates the app runtimes, managed dependencies, and a workspace service using the `zcp@1` service type. In this recipe it appears as the `zcp` service. That service is where the agent, terminal, and browser IDE run. App code still deploys to the app runtime services, not to `zcp`. +## 2. Authorize Claude Code +When provisioning finishes, the dashboard opens the Claude Code authentication flow. Use your own Claude Code subscription login or API credentials. +This is separate from the ZCP project token. Zerops injects the project-scoped `ZCP_API_KEY` into the workspace service; your Claude account or API key is used only by the bundled agent. +If you close the prompt, open the project in the dashboard and then open the `zcp` service. The control-plane panel shows the browser workspace, web terminal, SSH path, desktop IDE path, and agent authorization state. +## 3. Open the workspace +After authentication, continue into **Browser VS Code**. The workspace opens with the project filesystem, ZCP configuration, terminal access, and the Claude Code panel available in the editor. +You are now inside the remote workspace. The agent can read project state, use project-scoped ZCP operations, reach project-private services, and deploy app changes to the runtime services created by the recipe. +## 4. Ask for a product outcome +In Claude Code, ask for the app behavior in natural language. A good first prompt is intentionally short: ```text -Build a task board where tasks stay saved after refresh. +Build a task board for my team. +Tasks should stay saved after refresh. ``` -Add constraints only when they change the product, runtime layout, or delivery path: +The agent is expected to deploy, verify, read evidence, and return proof for the app task. Reserve extra prompt detail for decisions that change the work. +Add details when they change the product, stack, runtime layout, acceptance criteria, or delivery path: ```text -Build a task board where tasks stay saved after refresh. Use the existing dev+stage pair if this project has one. +In this Laravel app, add a task board backed by the existing PostgreSQL service. +A user can create a task, refresh the page, and still see it. ``` -```text -After it works, set up git-push delivery to git@github.com:my-org/task-dashboard.git. -``` -## What you should see -A good run should make these points clear: -1. The agent read current project state before changing anything. -2. It named the app runtime it will change. The `zcp` service is the ZCP setup, not the app target. -3. It used existing services when they already fit, or created missing services before app work. -4. It changed code and `zerops.yaml` as needed. -5. It deployed through Zerops and read logs or events if something failed. -6. It verified both platform reachability and the requested dashboard behavior. -7. It ended with a URL or a concrete blocker. -Open the URL the agent gives you. If the page loads but the requested behavior is missing, the task is not done; ask the agent to verify that exact behavior again. +:::note Prompt shape +Short prompts work when they describe the product outcome. Add stack, runtime layout, acceptance criteria, delivery preference, external credentials, or destructive-action approval only when those decisions matter. ZCP carries project discovery, Zerops wiring, deploy, verification, evidence reading, and recovery behind that request. +::: +## 5. Read the proof +The final answer should give you a real URL, endpoint, UI state, stored result, or a concrete blocker. Open the URL and try the core behavior the agent says it verified. +For the task-board prompt, create a task, refresh the page, and confirm the task is still there. If it disappears, the app is not done; ask the agent to verify that exact behavior again and continue from the project evidence rather than starting over. +If the agent cannot finish, the useful output is a concrete blocker: the missing credential, decision, unsupported runtime choice, or repeated failure it could not recover from. ## Next steps -- [How ZCP works](/zcp/concept/how-it-works) - the model behind the run. -- [Build with ZCP](/zcp/workflows/build-with-zcp) - how real app work flows. -- [Choose how finished work ships](/zcp/workflows/delivery-handoff) - direct deploy, git-push, or external handoff. -## Gotchas -- **Deploy success is not done.** The app behavior you requested must be verified on the real URL or endpoint. -- **The `zcp` service is not the app.** Runtime services such as `appdev`, `appstage`, or `app` receive app code and deploys. -- **Production stays out of the agent loop.** Use ZCP in dev/staging projects; promote to production through your CI or release process. +Read [How ZCP works](/zcp/concept/how-it-works) if you want the model behind what just happened: project state, runtime fit, app wiring, deploy evidence, behavior proof, and blockers. +Read [Remote or local ZCP setup](/zcp/setup/choose-workspace) when you are deciding where the agent workspace should live for a real project. Remote setup keeps the agent inside Zerops; local setup keeps the agent next to your local files, editor, data, and tools. +Read [Build with ZCP](/zcp/workflows/build-with-zcp) before giving the agent larger tasks. It explains the practical choices you can control during normal work: runtime layout, development context, and delivery preference. +Before promoting anything to production, read [Production boundary](/zcp/security/production-policy). ZCP belongs in development or staging projects; production should stay in a separate Zerops project and receive verified work through your release path. ---------------------------------------- # Zcp > Reference > Agent Workflow -Exact vocabulary for ZCP workflows: runtime layout, verification, delivery, failure state, and completion evidence. Day-to-day app prompts should use outcomes; policies and handoffs can use these names when precision matters. +Exact vocabulary for ZCP workflows. Day-to-day prompts should describe outcomes; reference, audits, policies, and handoffs can use these names when precision matters. ## Session layers -| Layer | What it is | What changes here | -|---|---|---| -| ZCP setup | Where ZCP runs: remote `zcp@1` service or local `zcp` binary. | No app code should be deployed to the setup itself. | -| Target runtime service | The app runtime: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys. | -| Managed services | Databases, caches, queues, search, storage. | Schema/data operations, never app code deploys. | -| Managed services | Databases, caches, queues, search, storage. | Schema/data operations, never app code deploys. | +Most workflow mistakes come from confusing these layers: +| Layer | What it is | What changes here | +| ---------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------- | +| ZCP setup | Where ZCP runs: remote `zcp@1` service or local `zcp` binary. | No app code should be deployed to the setup itself. | +| Target runtime service | The app runtime: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys. | +| Managed services | Databases, caches, queues, search, storage, mail. | Schema/data operations and credentials, never app code deploys. | +| Managed services | Databases, caches, queues, search, storage, mail. | Schema/data operations and credentials, never app code deploys. | The `zcp` service is the control surface, not the app runtime. -## Service setup -Before code work starts, ZCP should identify the app runtime services and managed dependencies. It should use existing services when they fit, create missing services when the project is empty, and resume from live project state if setup was interrupted. -The service setup phase stops before app edits begin; the full ZCP work session then continues into code, deploy, verification, and recovery. -## App work completion contract -For an app intent, ZCP expects the agent to handle code, config, deploy, verify, and retries behind the prompt. A completed session leaves evidence of: -- runtime scope, -- app code and `zerops.yaml` changes, -- a direct deploy for the first verified runtime, -- platform reachability, -- requested behavior on the real endpoint or UI, -- evidence-based retries when something failed, -- a URL or blocker. -## Verification layers -| Layer | What it proves | -|---|---| -| Runtime reachability | Service status, recent error logs, and HTTP probe for eligible runtime services. | -| Requested behavior | The user-requested endpoint, UI state, job result, database state, or stage URL works. | -| Requested behavior | The user-requested endpoint, UI state, job result, database state, or stage URL works. | -Both layers must pass before a session reports completion. +## Service setup routes +Before app code work starts, ZCP reads project state and chooses a setup route. The route is an implementation detail in first-read docs, but it is useful in reference and audits. +```mermaid +flowchart TD + start(["Read project state"]) + mid{"Interrupted setup +to resume?"} + runtimes{"Runtime services +already exist?"} + known{"Request matches +known recipe/stack?"} + resume(["resume"]) + adopt(["adopt"]) + recipe(["recipe"]) + classic(["classic"]) + start --> mid + mid -- yes --> resume + mid -- no --> runtimes + runtimes -- yes --> adopt + runtimes -- no --> known + known -- yes --> recipe + known -- no --> classic +``` +| Route | Use when | Wrong signal | +| --------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | +| `adopt` | Runtime services already exist, including recipe-created projects. | Recreating services that exist, or targeting `zcp` as the app. | +| `recipe` | The project is empty or only has ZCP, and the request matches a known stack recipe. | Deploying an unchanged starter as if it were the requested product. | +| `classic` | The project is empty and needs a custom service plan. | Writing app code before service ownership and runtime scope are known. | +| `resume` | A previous setup was interrupted. | Starting from scratch without acknowledging live services and saved state. | +| `resume` | A previous setup was interrupted. | Starting from scratch without acknowledging live services and saved state. | +Service setup stops when app runtimes and managed dependencies are known. App code, `zerops.yaml`, and the first deploy belong to the develop flow. +## Develop flow +The develop loop is the main ZCP work cycle. It closes only when reachability and requested behavior both pass. +```mermaid +flowchart TD + scope["1. Name runtime scope"] + change["2. Change code/config"] + deploy["3. Deploy in-scope runtime"] + reach{"4. Runtime reachability +passes?"} + behavior{"5. Requested behavior +passes?"} + done(["Done: proof or URL"]) + fix["6. Categorize failure, +read evidence, fix"] + blocker(["Blocker"]) + scope --> change --> deploy --> reach + reach -- yes --> behavior + reach -- no --> fix + behavior -- yes --> done + behavior -- no --> fix + fix --> deploy + fix --> blocker +``` +1. **Name runtime scope.** State which runtime is in scope (`appdev`, `appstage`, `app`, or linked local target). In dev+stage projects, dev work does not imply stage unless requested. +2. **Change code/config.** Edit app files, `zerops.yaml`, env references, migrations, seeds, or framework config. +3. **Deploy directly first.** The first verified runtime deploy uses the direct ZCP deploy path. Delivery setup is applied after a verified running result exists. +4. **Verify runtime reachability.** Service status, recent error logs, and HTTP probe for eligible runtime services. +5. **Verify requested behavior.** Endpoint body, UI state, job result, persisted data, or another check tied to the user request. +6. **Fix from evidence.** Read classification, logs, events, and check output. Repeating the same deploy without new evidence is not progress. +Dynamic dev runtimes may need an explicit start or restart after deploy. Built-in webserver runtimes do not need a separate dev-server step unless the framework requires one. ## Runtime layouts -| Layout | Meaning | -|---|---| -| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage must be named when included. | -| `dev` | One mutable development runtime. | -| `simple` | One runtime with no dev/stage split. | -| `local-stage` | Local checkout linked to one Zerops runtime for deploys. | -| `local-only` | Local checkout with no linked deploy target yet. | -| `local-only` | Local checkout with no linked deploy target yet. | Runtime layout describes which app runtime services ZCP should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. -## Delivery choice -Delivery choice applies after a verified deploy and describes future changes. -| Choice | Meaning | -|---|---| -| Direct deploy | ZCP keeps deploying changes directly to the target runtime. | -| Git push | The agent commits and pushes to a configured remote; any resulting build still needs verification. | -| External handoff | Your CI, release process, or a human owns future delivery. | -| External handoff | Your CI, release process, or a human owns future delivery. | +| Layout | Meaning | Typical names | +| ------------- | ---------------------------------------------------------------------------------------------- | ---------------------------- | +| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage is explicit. | `appdev` + `appstage` | +| `dev` | One mutable development runtime. | `appdev` | +| `simple` | One runtime with no dev/stage split. | `app` | +| `local-stage` | Local source directory linked to one Zerops runtime for deploys. | local directory + `appstage` | +| `local-only` | Local source directory with no linked deploy target yet. | local directory | +| `local-only` | Local source directory with no linked deploy target yet. | local directory | +The stage hostname is supplied by project state. ZCP should not invent it from the dev hostname. +## Delivery modes +Delivery mode applies after a verified deploy and describes how future changes ship. It does not redirect the first successful deploy. +| Exact mode | User-facing choice | Meaning | +| ---------- | ------------------ | ------------------------------------------------------------------------------------------------------------------ | +| `auto` | Keep direct deploy | ZCP keeps deploying future changes directly to the target runtime. | +| `git-push` | Push to git | The agent commits and pushes to a configured remote; any resulting build still needs observation and verification. | +| `manual` | External handoff | CI, release process, or a human owns future delivery; ZCP records evidence but does not initiate the next deploy. | +| `manual` | External handoff | CI, release process, or a human owns future delivery; ZCP records evidence but does not initiate the next deploy. | +Git-push capability, delivery mode, and build integration are separate. A project can have git-push configured while still using direct deploy for a given session. ## Failure categories -| Category | Meaning | Read first | -|---|---|---| -| `build` | Build phase failed. | Build logs and build commands. | -| `start` | Build passed, runtime failed to start or crashed. | Runtime/prepare logs and start command. | -| `verify` | Runtime exists but reachability or behavior failed. | Failing check detail and runtime logs at request time. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and network path. | -| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Structured rejection detail and config files. | -| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | -| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | -| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +| Category | Meaning | Read first | +| ------------ | --------------------------------------------------------------------- | ------------------------------------------------------ | +| `build` | Build phase failed. | Build logs and build commands. | +| `start` | Build passed, runtime failed to start or crashed. | Prepare/runtime logs and start command. | +| `verify` | Runtime exists but reachability or behavior failed. | Failing check detail and runtime logs at request time. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and network path. | +| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Structured rejection detail and config files. | +| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | +| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +Categorization is what turns retries into evidence-driven fixes. Recovery moves and symptom mapping live in [Troubleshooting](/zcp/reference/troubleshooting). ## Completion evidence -A well-run session can answer: +A completed app task can answer: - which services were used or created, -- which runtime scope was changed, +- which runtime scope changed, - which deploy passed, -- which reachability and behavior checks passed, -- which URL or endpoint proves the result, -- which delivery contract applies next, +- which reachability check passed, +- which user-requested behavior passed, +- which URL, endpoint, UI state, or stored result proves it, +- which delivery mode applies next, - or which blocker remains and what evidence supports it. +A green build with a broken route is not completion. A clear blocker is acceptable completion only when it names the failure category, evidence read, fixes tried, and human decision or credential still needed. +## Practical rules +- Use existing services when they fit; do not recreate them just to get a clean slate. +- Bootstrap/service setup is not app work; app files and first deploy happen in develop. +- First functional deploy is direct; git/CI/handoff comes after proof. +- Managed services are dependencies, not deploy targets. +- Stage is explicit in `standard` projects; dev changes do not silently promote. +- Runtime health and requested behavior are separate verification gates. +- Stop on repeated failure without new evidence, missing credentials, ambiguous runtime scope, or destructive recovery. +## Auditing a session +A session is well-shaped if platform evidence and the agent report answer: +| Question | Evidence | +| ----------------------------------------------- | ------------------------------------------------------------------------- | +| Which setup route and runtime layout were used? | ZCP status, service metadata, project services. | +| Where did service setup end and app work begin? | Work-session state, deploy history, file changes. | +| Which runtime was deployed and verified? | Service events, deploy result, verify output. | +| What behavior proved success? | Endpoint/UI/job/data proof in the final report. | +| What controls future delivery? | Delivery mode, git-push state, build integration, or manual handoff note. | +| What controls future delivery? | Delivery mode, git-push state, build integration, or manual handoff note. | Operation-name reference lives in [Advanced operations](/zcp/reference/mcp-operations). Recovery shortcuts live in [Troubleshooting](/zcp/reference/troubleshooting). ---------------------------------------- @@ -21803,7 +22014,7 @@ Operation-name reference lives in [Advanced operations](/zcp/reference/mcp-opera Exact terms, operation names, and recovery detail live here. Day-to-day app work starts in [Build with ZCP](/zcp/workflows/build-with-zcp). | Need | Page | |---|---| -| Name the workflow phases, runtime layouts, verification layers, and delivery choices | [Workflow terms](/zcp/reference/agent-workflow) | +| Name service setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence | [Workflow terms](/zcp/reference/agent-workflow) | | Look up common MCP operation names for agent-client policy or debugging | [Advanced operations](/zcp/reference/mcp-operations) | | Turn a running service into a re-importable bundle | [Package a running service](/zcp/workflows/package-running-service) | | Recover a stuck or confused session | [Troubleshooting](/zcp/reference/troubleshooting) | @@ -21816,55 +22027,55 @@ Exact terms, operation names, and recovery detail live here. Day-to-day app work Most users never need operation names. They matter when you configure agent-client policy, debug an MCP integration, or automate ZCP directly. In normal app work, ask the agent for outcomes. Recipe-authoring and ZCP-internal maintenance operations are intentionally out of scope. ## Permission classes -| Class | Meaning | -|---|---| -| Read-only | Inspect state, logs, events, guidance, or verification output. | -| Mutating | Change services, env vars, scaling, deploy state, or lifecycle. | +| Class | Meaning | +| ----------------- | ----------------------------------------------------------------------------------------- | +| Read-only | Inspect state, logs, events, guidance, or verification output. | +| Mutating | Change services, env vars, scaling, deploy state, or lifecycle. | | Operational setup | Coordinate workflows, mounts, imports, recipes, delivery setup, or generated local files. | | Operational setup | Coordinate workflows, mounts, imports, recipes, delivery setup, or generated local files. | ## Read-only operations -| Operation | Purpose | -|---|---| -| `zerops_discover` | Read services, ports, env-var keys, and current state. | -| `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | -| `zerops_events` | Read service-scoped project activity, deploys, builds, scaling, and failures. | -| `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | -| `zerops_knowledge` | Fetch ZCP guidance or platform knowledge for the current state. | -| `zerops_process` | Check a known async process; cancel is a mutating action. | -| `zerops_process` | Check a known async process; cancel is a mutating action. | +| Operation | Purpose | +| ------------------ | ----------------------------------------------------------------------------- | +| `zerops_discover` | Read services, ports, env-var keys, and current state. | +| `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | +| `zerops_events` | Read service-scoped project activity, deploys, builds, scaling, and failures. | +| `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | +| `zerops_knowledge` | Fetch ZCP guidance or platform knowledge for the current state. | +| `zerops_process` | Check a known async process; cancel is a mutating action. | +| `zerops_process` | Check a known async process; cancel is a mutating action. | ## Mutating operations -| Operation | Purpose | -|---|---| -| `zerops_deploy` | Ship code through the Zerops build and deploy pipeline. | -| `zerops_env` | Read, set, delete, or generate env vars and local `.env` files. | -| `zerops_manage` | Start, stop, restart, reload, or connect storage. | -| `zerops_scale` | Change CPU, RAM, disk, CPU mode, or container autoscaling where supported. `HA`/`NON_HA` is set at service creation. | -| `zerops_subdomain` | Enable or disable public subdomain access. | -| `zerops_delete` | Delete a service. Explicit named approval is required. | -| `zerops_delete` | Delete a service. Explicit named approval is required. | +| Operation | Purpose | +| ------------------ | -------------------------------------------------------------------------------------------------------------------- | +| `zerops_deploy` | Ship code through the Zerops build and deploy pipeline. | +| `zerops_env` | Read, set, delete, or generate env vars and local `.env` files. | +| `zerops_manage` | Start, stop, restart, reload, or connect storage. | +| `zerops_scale` | Change CPU, RAM, disk, CPU mode, or container autoscaling where supported. `HA`/`NON_HA` is set at service creation. | +| `zerops_subdomain` | Enable or disable public subdomain access. | +| `zerops_delete` | Delete a service. Explicit named approval is required. | +| `zerops_delete` | Delete a service. Explicit named approval is required. | ## Operational setup -| Operation | Purpose | -|---|---| -| `zerops_workflow` | Track, recover, or configure ZCP work sessions; also carries the package-running-service export flow. | -| `zerops_import` | Import project/service definitions. Destructive override is gated. | -| `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | -| `zerops_preprocess` | Expand Zerops preprocessor expressions. | -| `zerops_export` | Read platform project/service export YAML and service metadata. | -| `zerops_export` | Read platform project/service export YAML and service metadata. | +| Operation | Purpose | +| ------------------- | ----------------------------------------------------------------------------------------------------- | +| `zerops_workflow` | Track, recover, or configure ZCP work sessions; also carries the package-running-service export flow. | +| `zerops_import` | Import project/service definitions. Destructive override is gated. | +| `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | +| `zerops_preprocess` | Expand Zerops preprocessor expressions. | +| `zerops_export` | Read platform project/service export YAML and service metadata. | +| `zerops_export` | Read platform project/service export YAML and service metadata. | ## Remote and local differences -| Area | Remote setup | Local setup | -|---|---|---| -| Files | Runtime files through SSHFS/project containers | Local working directory | -| Deploy source | Project service or batch deploy inside Zerops | Local `workingDir` | -| Dev server | ZCP can run/inspect project-container dev processes | Your local tool owns the dev server | -| Env bridge | Project env is available in the project | `.env` generation resolves project references for local app use | -| Git | Workspace-managed credentials | User's local git credentials | -| Git | Workspace-managed credentials | User's local git credentials | +| Area | Remote setup | Local setup | +| ------------- | --------------------------------------------------- | --------------------------------------------------------------- | +| Files | Runtime files through SSHFS/project containers | Local working directory | +| Deploy source | Project service or batch deploy inside Zerops | Local `workingDir` | +| Dev server | ZCP can run/inspect project-container dev processes | Your local tool owns the dev server | +| Env bridge | Project env is available in the project | `.env` generation resolves project references for local app use | +| Git | Workspace-managed credentials | User's local git credentials | +| Git | Workspace-managed credentials | User's local git credentials | ## Confirmation gates Two operations require explicit care: - **Service deletion** requires explicit user approval in the current conversation, by service name. - **Destructive import override** first refuses and names what would be replaced; a second call must acknowledge the same targets. -See [Tokens and credentials](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions) for the user-facing confirmation flow. +See [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) for the user-facing confirmation flow. ---------------------------------------- @@ -21874,35 +22085,87 @@ Start recovery from current project state, not from chat memory. ```text Read ZCP status and tell me where this project stands before changing anything. ``` -That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. -## Failure categories -| Category | Meaning | First move | -|---|---|---| -| `build` | Build failed before a runtime could start. | Read build logs and `zerops.yaml` build steps. | -| `start` | Build passed, runtime failed to start or crashed. | Read runtime/prepare logs and check start command, ports, and env vars. | -| `verify` | Runtime exists, but reachability or requested behavior failed. | Read the failing check, HTTP response, and runtime logs at request time. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Check the named network path and, for local setup, VPN. | -| `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Read the structured rejection and fix the named field. | -| `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | Fix the named credential; do not rotate unrelated tokens. | -| `other` | No known category matched. | Read raw events/logs and stop if the same unknown failure repeats. | -| `other` | No known category matched. | Read raw events/logs and stop if the same unknown failure repeats. | -## Common symptoms -| Symptom | Likely move | -|---|---| -| Build log shows missing command/module/lock file | Fix build commands, manifests, deploy files, or dependencies. | -| Runtime is `READY_TO_DEPLOY` or restarts continuously | Read runtime logs; the start command or env contract is usually wrong. | -| Public URL returns 502 or connection error | Check port binding, `0.0.0.0`, subdomain readiness, and whether the service is HTTP-eligible. | -| Deploy succeeded but feature is broken | Treat it as behavior verification failure, not deploy success. | -| Local app cannot reach `db`, `redis`, or storage | Bring VPN up and regenerate `.env` if project env changed. | -| Agent targets `zcp` as the app | Correct it to the runtime service. | -| Agent loops on the same failure | Ask for category, evidence read, fixes attempted, and the next human decision. | -| Agent loops on the same failure | Ask for category, evidence read, fixes attempted, and the next human decision. | +That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. ZCP can rebuild the project picture from live services, recent deploys, logs, events, and saved work-session state. +## Start from the failure category +When a deploy or verify step fails, ZCP should surface a category with the likely cause and next move. Categories are recovery-shaped. `network` does not mean "the network is broken"; it means the first useful move is connectivity, VPN, SSH, DNS, or public-route evidence rather than editing app code. +| Category | Meaning | Read first | Recovery move | +| ------------ | --------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `build` | Build failed before runtime start. | Build logs and `zerops.yaml` build steps. | Fix build commands, dependency manifests, lock files, deploy files, or build resources. Runtime logs will not explain this failure. | +| `start` | Build passed, runtime failed to start or crashed. | Prepare logs or runtime logs, whichever the failure names. | Check `run.start`, ports, env references, `prepareCommands`, and whether the process binds `0.0.0.0` rather than `127.0.0.1`. | +| `verify` | Runtime exists, but reachability or requested behavior failed. | The failing check, HTTP response, and runtime logs at request time. | Treat it as app behavior or route recovery, not deploy success. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | The transport error and the named network path. | Check VPN for local setup, service status for remote setup, SSH transfer state, DNS/subdomain readiness, or platform timeout. | +| `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Structured rejection detail, field path, or API metadata. | Fix the named setup block, env reference, `deployFiles`, service type, or prerequisite. | +| `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | The credential surface named in the error. | Replace that credential only: `ZCP_API_KEY`, `GIT_TOKEN`, local git auth, `ZEROPS_TOKEN`, SSH key, or app secret. | +| `other` | No known category matched. | Raw reason, service events, and logs for the named phase. | Stop if the same unknown reason repeats; report the raw reason and recent evidence. | +| `other` | No known category matched. | Raw reason, service events, and logs for the named phase. | Stop if the same unknown reason repeats; report the raw reason and recent evidence. | +Build logs and runtime logs are different streams. A build failure shows in the build container; a runtime crash shows in runtime logs. If the agent reads the wrong stream, the diagnosis will be wrong even when the log command succeeded. +## Deploy and start symptoms +| Symptom | Likely category | Next move | +| --------------------------------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------- | +| Build exits with "command not found", missing module, npm 404, or compile error | `build` | Read `buildCommands`, manifests, lock files, package manager, and deploy file list. | +| Build or SSH transfer ends with `signal: killed` | `build` / `network` | Treat it as memory pressure first: scale build/source resources or reduce the heavy step. | +| Runtime crashes immediately with `EADDRINUSE`, missing module, or missing env var | `start` | Read runtime logs scoped to the failed start and check the start command/env contract. | +| Database connection refused during init/start | `start` / `network` | Confirm the managed service is running and env references such as `${db_*}` resolve. | +| Preflight refuses `INVALID_ZEROPS_YML` or setup mismatch | `config` | Read the field-level rejection and fix that setup entry; setup names are not always hostnames. | +| Git push returns authentication failed | `credential` | Remote setup needs `GIT_TOKEN`; local setup uses your local git credentials. | +| Git push returns authentication failed | `credential` | Remote setup needs `GIT_TOKEN`; local setup uses your local git credentials. | +Useful prompt: +```text +Show me the failure category, build logs, runtime logs, and recent events for the last failed deploy of appdev. +``` +## Verify and public URL symptoms +A deploy can be green and still fail verification. Verify has two layers: platform reachability, then the requested behavior. +| Symptom | Likely cause | Next move | +| ------------------------------------------------------------ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| Service is `RUNNING`, but the route returns 500 or 404 | App route, dependency, or request handling is broken. | Read runtime logs at request time and verify the requested route/body, not only `/`. | +| Public URL returns connection error right after first deploy | Subdomain route is still propagating, or the runtime is not bound correctly. | Wait 30-60 seconds and retry; if it persists, check port binding and public-access eligibility. | +| 502 persists after deploy and wait | Start command binds `127.0.0.1`, wrong port, or HTTP server not listening. | Check `zerops.yaml` `run.ports[]` and make the app bind the same port on `0.0.0.0`. | +| Subdomain stays disabled | Worker or non-HTTP service, or explicit public access missing. | Workers do not get useful public URLs. If it is an HTTP runtime, ask the agent to enable subdomain access and verify it. | +| Requested feature fails after a successful deploy | Behavior verification failed. | Continue the loop from logs/events/check output; do not report the task done. | +| Requested feature fails after a successful deploy | Behavior verification failed. | Continue the loop from logs/events/check output; do not report the task done. | Public subdomain access is enabled on first deploy for eligible HTTP runtimes. Workers and non-HTTP services do not get a useful public URL by design. +## Token and credential symptoms +Token problems usually happen at startup. The fix is to replace the token on the surface ZCP reads, then restart the agent so the new process inherits it. +| Symptom | Likely cause | Next move | +| -------------------------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `Token accesses N projects; use project-scoped token` | Multi-project or account-wide token. | Generate a token scoped to exactly one project and replace `ZCP_API_KEY`. | +| `Token has no project access` | Token authenticates but reaches no project. | Grant project access or generate a new project-scoped token. | +| `No authentication found: set ZCP_API_KEY or log in with zcli` | No token reached ZCP. | Add `ZCP_API_KEY` under `.mcp.json` `env` for local setup, or check remote service env. | +| API replies with 401 or `AUTH_TOKEN_EXPIRED` | Token expired or was revoked. | Generate a new project-scoped token, replace it, restart the agent. | +| GitHub Actions deploy fails on auth | `ZEROPS_TOKEN` missing/wrong. | Set a Zerops API token as the repository secret named `ZEROPS_TOKEN`. It is not a GitHub token. | +| GitHub Actions deploy fails on auth | `ZEROPS_TOKEN` missing/wrong. | Set a Zerops API token as the repository secret named `ZEROPS_TOKEN`. It is not a GitHub token. | +Full credential model: [Tokens and credentials](/zcp/security/tokens-and-project-access). +## Local setup symptoms +These apply to local setup. Remote setup is already on the project-private network and does not need VPN from your laptop. +| Symptom | Likely cause | Next move | +| --------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | +| Local app cannot reach `db`, `redis`, `cache`, or storage hostname | VPN is down or dropped after sleep/network change. | Run `zcli vpn up ` again. | +| `no route to host` or connection refused for managed-service hostname | Same network path issue. | Bring VPN up, then retry. If network works, regenerate `.env`. | +| Local app reads stale project credentials | `.env` is a snapshot. | Ask ZCP to regenerate the `.env` after project env changes. | +| Re-running `zcp init` made ZCP disappear from the agent | `.mcp.json` was rewritten without `ZCP_API_KEY`. | Re-add the `env` block and restart the agent from the project root. | +| Agent does not list the `zerops` MCP server | Agent launched from the wrong directory or config not loaded. | Quit and relaunch from the directory containing `.mcp.json`. | +| `zcp` is not found after install | Install path is not on `PATH`. | Add `~/.local/bin` or the install target to your shell `PATH`, then restart the shell/agent. | +| `zcp` is not found after install | Install path is not on `PATH`. | Add `~/.local/bin` or the install target to your shell `PATH`, then restart the shell/agent. | +## Session drift and manual takeover +Use status when the chat-side picture drifts from reality: +```text +Read ZCP status, list the services in scope, and summarize the last deploy and verify result before changing anything. +``` +That is the right move when the agent says it has no context, a browser workspace was reopened, a CLI session restarted, or you are taking over from a previous run. +For manual takeover, read evidence in this order: +| Evidence | Why it matters | +| --------------------------- | ------------------------------------------------------------------------------------------------- | +| Service-scoped events | Shows deploys, build/start transitions, failed process reasons, scaling, lifecycle actions. | +| Build logs and runtime logs | Separates build failures from runtime crashes and request-time app errors. | +| Verify output | Shows whether reachability and requested behavior actually passed. | +| Git history | Matters when delivery uses git-push; shows what source change corresponds to the deployed result. | +| Git history | Matters when delivery uses git-push; shows what source change corresponds to the deployed result. | ## When to stop Stop the loop when the agent cannot make progress with new evidence, needs a missing credential, is about to delete or replace a service, or the target runtime/stage is ambiguous. +A repeated retry with the same evidence is not recovery. Ask the agent for the category, evidence read, fixes attempted, and the next human decision. Before destructive recovery, read service-scoped events, logs, deploy/verify result, and git history when delivery uses git-push. ## Related -- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) +- [Build with ZCP](/zcp/workflows/build-with-zcp) - [Workflow terms](/zcp/reference/agent-workflow) - [Tokens and credentials](/zcp/security/tokens-and-project-access) - [Set up local ZCP](/zcp/setup/local-agent-bridge) @@ -21911,77 +22174,123 @@ Before destructive recovery, read service-scoped events, logs, deploy/verify res # Zcp > Security > Production Policy -Rule: keep ZCP in a development or staging Zerops project. Production should be a separate project without a `zcp` service; promotion happens through CI or a release pipeline with production-scoped credentials. -The platform doesn't stop you from adding a `zcp` service to a production project. Treat the dev/staging boundary as load-bearing because crossing it puts an agent inside production, not because Zerops will refuse the configuration. -## Why a separate production project -The clean separation is the project boundary: ZCP runs where the agent works, while production runs in its own 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 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 with no `zcp` service, 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 architecture pattern production uses — same runtime version, same managed service types, same build/deploy path — before promotion to the separate production project. -Use stage as the production-like rehearsal: -- The agent develops on dev (`appdev` in the `standard` runtime layout). -- 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-like failures show up. +Rule: use ZCP in development or staging projects. Production should be a separate Zerops project without a `zcp` service, and production deploys should come from CI, a release pipeline, or a deliberate human `zcli` push using production-scoped credentials. +Zerops does not prevent you from adding a `zcp` service to production. The policy exists because ZCP gives a coding agent project-scoped operational access. In production, that is the wrong blast radius for normal app development. +## Recommended project layout +| Project | Contains | Who operates it | +| ----------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------ | +| Development / staging project | `zcp` service, agent workspace, dev runtime, optional stage runtime, non-production managed services | Agent plus humans | +| Production project | Production runtimes, production managed services, production env values, production backups and scaling | CI/release process plus humans | +| Production project | Production runtimes, production managed services, production env values, production backups and scaling | CI/release process plus humans | +The two projects use different Zerops tokens. A token scoped to the development project cannot reach production. A production token should not be placed in the development `zcp` service or a local `.mcp.json` used by the agent. +## Why production is a separate project +- **The project is the security boundary.** ZCP binds to one project at startup. Keeping production in another project prevents a development agent from touching production by accident. +- **Secrets stay clean.** Production env values, database credentials, object-storage keys, and third-party secrets stay in the production project. +- **Operational policies can differ.** Production often needs HA services, stronger backup retention, stricter scaling, alerts, and release approvals. Development can stay cheaper and more flexible. +- **Audit trails stay readable.** Development experiments and agent retries do not mix with production deploy evidence. +This separation matters even when the same source repository deploys to both projects. +## What stage proves +Stage is the production-like rehearsal inside the development/staging project. It is where the agent proves the change before promotion leaves the ZCP project. +Use stage to match production where it matters: +- same runtime family and version, +- same managed service types, +- same build and start command pattern, +- same deploy path, +- behavior checks against a real running service. +Stage is not production. It should use non-production data and non-production secrets. A green stage means the change is ready for the production release path, not that the agent should deploy to production itself. ## 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. +After stage verifies, ZCP's job is done for that change. Promotion to production happens outside the agent loop: +- CI deploys a merged commit or release tag to the production project. +- A release pipeline runs `zcli push` with a production-scoped `ZEROPS_TOKEN`. +- A human runs `zcli push` against the production project with production credentials. +The agent can prepare the handoff by pushing code, summarizing verification evidence, and naming the runtime and URL it verified. It should not bridge the development project to the production project. 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 operational access to 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-like 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 ZCP setup | -| Run the agent's deploys directly into production from a development session | Production deploys come from the production project's pipeline, not the development ZCP setup | +## Credential rules +| Credential | Production policy | +| ----------------------------------- | -------------------------------------------------------------------------------------------------------- | +| Development `ZCP_API_KEY` | Never grants production access. Keep it scoped to the development/staging project. | +| Production `ZEROPS_TOKEN` | Store only in the production CI/release secret store. Do not place it in the development `zcp` service. | +| Agent subscription or model API key | May be used by the agent, but it does not grant Zerops production access by itself. | +| Git credentials | May push source changes, but production deploy authority should still be controlled by the release path. | +| Git credentials | May push source changes, but production deploy authority should still be controlled by the release path. | +If a production deploy fails, investigate in the production project with production logs, events, backups, and CI output. Do not attach the development agent directly to production as a shortcut. +## Production separation rules +| Rule | Why it matters | +| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------- | +| Keep the production project without a `zcp` service | Production should not host an agent workspace with project-scoped operational access. | +| Keep production tokens out of local `.mcp.json` | Local agents should operate development or staging projects, not production. | +| Keep production `ZEROPS_TOKEN` in the release secret store | Development tooling should not deploy to production outside the release path. | +| Treat stage proof as release evidence, not production approval | Stage is the rehearsal; production approval is a separate release decision. | +| Promote through CI, release tooling, or a deliberate human action | Production execution should use the production credential boundary. | +| Promote through CI, release tooling, or a deliberate human action | Production execution should use the production credential boundary. | +## Acceptable agent involvement +The agent can still help before production promotion: +- make the code change in development, +- deploy and verify dev/stage runtimes, +- produce the URL, endpoint result, or UI proof it verified, +- push or prepare the code path your team uses for review, +- summarize release notes and known blockers for the human or CI handoff. +ZCP's involvement stops at the handoff. Production execution belongs to the production release path. ## 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. -- [Troubleshooting](/zcp/reference/troubleshooting) — recover or take over before promoting. -- [Backup](/features/backup) — the production data safety net that operates outside ZCP. -- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) — the dev/stage loop this policy assumes. +- [Trust model](/zcp/security/trust-model) - the project boundary that makes this policy enforceable. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - project-scoped token handling. +- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare development setup paths. +- [Build with ZCP](/zcp/workflows/build-with-zcp) - the dev/stage loop this policy assumes. +- [Backup](/features/backup) - production recovery outside ZCP. ---------------------------------------- # Zcp > Security > Tokens And Project Access -ZCP enforces the project boundary at three layers: token shape (one project, exactly), where the token lives (platform-injected for remote setup, in `.mcp.json` for local setup), and confirmation gates on operations that are not reversible from inside the conversation. -## Recommended token shape — one project, full access -ZCP requires a Zerops API token that resolves to **exactly one project** at startup. For normal agent workflows, use **Custom access per project** scoped to a single project with **Full access**. Read-only tokens can pass startup, but they fail as soon as the agent tries to deploy, write env vars, or operate services. -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. +ZCP uses a Zerops API token to operate exactly one project. The important rule is simple: `ZCP_API_KEY` is a project credential for ZCP, not an agent login, not a git token, and not a general account token. +Remote setup gets `ZCP_API_KEY` from the Zerops platform. Local setup reads it from `.mcp.json`. In both paths, ZCP validates the token at startup and refuses tokens that resolve to no project or multiple projects. +## Credential map +| Name | What it authorizes | Where it belongs | +| ---------------------------- | -------------------------------------------- | --------------------------------------------------------------------------- | +| `ZCP_API_KEY` | ZCP against one Zerops project | Remote: `zcp` service env injected by Zerops. Local: `.mcp.json` env block. | +| Agent login or model API key | The coding agent itself | The bundled agent in remote setup, or your local agent client. | +| `GIT_TOKEN` | Git push from remote setup to a git provider | Secret env var on the `zcp` service when remote git-push delivery needs it. | +| `ZEROPS_TOKEN` | `zcli` in GitHub Actions or external CI | Separate project-scoped Zerops token stored in the CI/release secret store. | +| `ZEROPS_TOKEN` | `zcli` in GitHub Actions or external CI | Separate project-scoped Zerops token stored in the CI/release secret store. | +Keeping these names separate prevents most setup and delivery failures. +## Recommended `ZCP_API_KEY` shape +Use a Zerops API token with **Custom access per project**, scoped to exactly one project, with **Full access** for normal agent work. +Read-only tokens can authenticate, but they fail as soon as the agent needs to deploy, write env vars, restart services, scale, or change public access. Account-wide or multi-project tokens are refused before the agent can operate. +To generate the token: +1. Open [Settings -> Access Tokens Management](https://app.zerops.io/settings/token-management). +2. Create a token and name it for the project, for example `zcp-`. +3. Choose **Custom access per project**. +4. Add exactly one project. +5. Set that project to **Full access** for normal ZCP work. +6. Create the token and copy the value. Zerops shows it only at creation time. +The token's blast radius equals the project and its granted permissions. Other projects, organization settings, and billing stay out of reach. Zerops [Roles & Permissions](/features/rbac#integration-tokens) remain the platform authority. ## Rejected token shapes -ZCP validates the token at startup and refuses wrong shapes before the agent can operate the project. -| Token shape | What ZCP does | Why | -|---|---|---| -| Multi-project, including account-wide full access | Refuses to start | One ZCP process equals one project; ZCP won't pick which one. | -| No project access | Refuses to start | 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 to start | The token no longer authenticates against the Zerops API. | -| Expired or revoked | Refuses to start | The token no longer authenticates against the Zerops API. | -The fix is always the same: generate a per-project token and replace `ZCP_API_KEY` on the surface ZCP reads from. In remote setup, that value is injected into the `zcp` service environment. In local setup, it is the `env` block in `.mcp.json`. +ZCP validates token shape at startup. +| Token shape | What happens | Fix | +| ----------------------------------- | ------------------------------------------- | -------------------------------------------------------------------- | +| Account-wide or multi-project token | ZCP refuses to start. | Generate a token scoped to exactly one project. | +| Token with no project access | ZCP refuses to start. | Grant one project or create a new project-scoped token. | +| Expired or revoked token | ZCP refuses to start or receives HTTP 401. | Replace the token and restart the agent or ZCP process. | +| Read-only project token | Startup may pass, but mutations fail later. | Use full access for normal agent work, or expect read-only behavior. | +| Read-only project token | Startup may pass, but mutations fail later. | Use full access for normal agent work, or expect read-only behavior. | +Common messages: +| Message | Meaning | +| -------------------------------------------------------------- | ------------------------------------------------- | +| `Token accesses N projects; use project-scoped token` | The token can see more than one project. | +| `Token has no project access` | The token authenticates but reaches no project. | +| `No authentication found: set ZCP_API_KEY or log in with zcli` | The `zcp` process did not receive a usable token. | +| `AUTH_TOKEN_EXPIRED` or HTTP 401 | The token expired, was revoked, or is invalid. | +| `AUTH_TOKEN_EXPIRED` or HTTP 401 | The token expired, was revoked, or is invalid. | +For ZCP setup, provide `ZCP_API_KEY`. `zcli` login is a diagnostic fallback, not the normal agent setup path. ## Where the token lives - remote vs local {#where-the-token-lives--remote-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: -| Setup | Where `ZCP_API_KEY` comes from | Who provisions it | -|---|---|---| -| Remote setup | The `zcp` service container env | Zerops platform - injected automatically when the service boots | -| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You - added manually after `zcp init` | -| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You - added manually after `zcp init` | -In remote setup, the service starts with `ZCP_API_KEY` populated. If **Include Coding Agent** is enabled, the bundled agent uses that environment automatically. -In local setup, `zcp init` writes a token-less `.mcp.json` template; you add the `env` block: +ZCP reads `ZCP_API_KEY` from its process environment at startup. It does not write the token somewhere else or exchange it for a derived credential. +| Setup | Where `ZCP_API_KEY` comes from | Who provisions it | +| -------------------------------------------- | ------------------------------------------------------- | -------------------------------------------------------- | +| [Remote setup](/zcp/setup/hosted-workspace) | The `zcp` service environment | Zerops injects it automatically when the service starts. | +| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You add it after `zcp init`. | +| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You add it after `zcp init`. | +In remote setup, do not hand-edit `ZCP_API_KEY`. Replace or rotate it through the Zerops-managed surface so the service keeps the intended project boundary. +In local setup, `zcp init` writes a token-less `.mcp.json`. Add the token manually: ```json { "mcpServers": { @@ -21995,466 +22304,546 @@ In local setup, `zcp init` writes a token-less `.mcp.json` template; you add the } } ``` -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 itself, against the Zerops API | Container env (remote) or `.mcp.json` env block (local) | -| `GIT_TOKEN` | A git push from remote setup to a remote (GitHub, GitLab) | Remote: secret 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 | -| `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 remote setup is the one pushing. In local setup, 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 `zcp` service 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** — an 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. Delivery setup should place it in the CI secret store for the repository that runs the deploy. +Add `.mcp.json` to `.gitignore`. Each project directory should have its own file and its own project token. Switching projects means switching directories, not editing one shared credential. +## Agent credentials are separate +The bundled agent in remote setup may ask you to sign in or provide a model API key. That is not `ZCP_API_KEY`. +Zerops wires the agent to the project through ZCP. It does not provide your model subscription, store your agent login, or rotate your agent provider credentials. Treat the agent account exactly as you would outside Zerops. +## Git and CI credentials +`GIT_TOKEN` matters only when remote setup pushes to a git remote. It authorizes git provider access from the `zcp` service. In local setup, your local git CLI uses your normal SSH key or credential helper, so ZCP does not need `GIT_TOKEN`. +`ZEROPS_TOKEN` is a Zerops API token used by GitHub Actions or another CI system when that system runs `zcli` against Zerops. It is not a GitHub token. Use a separate project-scoped delivery token so ZCP sessions and CI/release workflows can be named, rotated, and audited independently. +For production delivery, `ZEROPS_TOKEN` should be scoped to the production project and stored only in the production CI or release secret store. +With GitHub CLI, the secret shape is: +```bash +gh secret set ZEROPS_TOKEN -b "$ZEROPS_DELIVERY_TOKEN" +``` +Use the GitHub UI or your CI secret manager instead if your team does not allow local CLI secret writes. ## Rotation -Rotate in the Zerops dashboard, then propagate to the consuming surface: -- **Remote setup** — the service env value updates; ZCP picks up the new value the next time the `zcp` service boots or restarts. -- **Local setup** — paste the new token into the `env` block of `.mcp.json`, then restart your agent client 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. | Requires explicit approval in the same conversation, including the service name. Remote setup also blocks deleting the `zcp` service it is running in. | -| **Wholesale service replacement** from an import on a service with prior failed deploy history | First returns a summary of what would be replaced and refuses to continue. The agent must show that evidence before asking you to confirm. | -| **Wholesale service replacement** from an import on a service with prior failed deploy history | First returns a summary of what would be replaced and refuses to continue. The agent must show that evidence before asking you to confirm. | -ZCP itself adds confirmation only for the two operations above. Your agent client or team policy may still prompt before tool calls. Deploys, env changes, lifecycle actions, restarts, scaling, and public-access changes do not get an additional ZCP-specific confirmation gate. Most are reversible by another call. A few are operationally destructive even though they are not gated by ZCP (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. -- **Replace-as-reset** — replacing a service from an import 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. -Threat model and hard refusals: [Trust model](/zcp/security/trust-model). -## Gotchas -- **Multi-project tokens are refused at startup, not at first deploy.** An account-wide 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 in remote setup; `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), and review service-scoped events, logs, deploy results, and git history before approving destructive recovery. +Rotate in the Zerops dashboard, then update the surface that consumes the token. +| Surface | Rotation step | +| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| Remote `ZCP_API_KEY` | Reconfigure or redeploy the remote workspace through the dashboard-managed path, then restart the `zcp` service so the process gets the new value. | +| Local `ZCP_API_KEY` | Paste the new token into `.mcp.json`, then restart the local agent client. | +| `ZEROPS_TOKEN` in CI | Replace the repository or CI secret. The next workflow run uses the new value. | +| `GIT_TOKEN` | Replace the git-provider credential stored for remote setup. | +| `GIT_TOKEN` | Replace the git-provider credential stored for remote setup. | +Rotation is picked up on next process start or workflow run, not in the middle of a live agent session. +## What ZCP enforces for destructive actions +A valid token does not remove every guardrail. ZCP adds explicit confirmation for operations where the loss is not safely reversible from inside the conversation. +| Operation | Gate | +| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Service deletion | Requires explicit approval in the same conversation, including the service name. Remote setup also blocks deleting the `zcp` service it is running in. | +| Wholesale service replacement after failed deploy history | The first request refuses, surfaces what would be replaced, and requires the agent to read failure evidence before asking you to confirm. | +| Wholesale service replacement after failed deploy history | The first request refuses, surfaces what would be replaced, and requires the agent to read failure evidence before asking you to confirm. | +Deploys, env changes, lifecycle actions, restarts, scaling, and public-access changes do not get an additional ZCP-specific confirmation gate. They are normal project operations for a full-access token and should be reviewed through service events, logs, verification output, and team policy. +Approval from a previous chat does not carry forward. A new conversation needs a new approval. +## Evidence before destructive recovery +When a service has recent failure history, ZCP enforces one recovery rule: read the platform evidence before destroying or replacing the service. +The agent should inspect service events, build logs, runtime logs, and failure summaries, then either fix the cause or show you the evidence before asking for destructive confirmation. The point is to preserve the failure context the next session needs. +A service waiting for first code deploy is not the same thing as a failed service. The gate is about recorded failure history, not idle state. +Threat model and boundaries: [Trust model](/zcp/security/trust-model). +## Credential checks +- **Account-wide full-access tokens are refused.** ZCP needs one project, not a broad account credential. +- **`zcp init` regenerates `.mcp.json`.** Re-add `ZCP_API_KEY` after rerunning it. +- **`GIT_TOKEN` is not `ZCP_API_KEY`.** One authorizes git provider access; the other authorizes Zerops project operations. +- **`ZEROPS_TOKEN` in GitHub Actions is not a GitHub PAT.** It is a Zerops API token for `zcli`. +- **A rotated token needs a restart.** The live ZCP process keeps the old environment value until it starts again. +- **A successful confirmation is still destructive.** Backups, git history, and service events are your recovery evidence; ZCP does not auto-rollback a confirmed deletion or replacement. ## 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. -- [Set up local ZCP](/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`. +- [Trust model](/zcp/security/trust-model) - the project boundary this page enforces. +- [Use remote workspace](/zcp/setup/hosted-workspace) - automatic token injection. +- [Set up local ZCP](/zcp/setup/local-agent-bridge) - local `.mcp.json` token setup. +- [Production boundary](/zcp/security/production-policy) - why production gets separate credentials. +- [GitHub integration](/references/github-integration) - CI secret usage with Zerops. ---------------------------------------- # Zcp > Security > Trust Model -The security model starts with one rule: a ZCP process operates one Zerops project. Remote and local setup share that project boundary, but they expose different surroundings to the agent. -## Remote setup or local setup -**Remote setup** is the project-contained path. -- ZCP runs in a `zcp@1` service inside the Zerops project. -- When **Include Coding Agent** is enabled, the bundled agent CLI runs there and is preconfigured to use ZCP. -- It uses a project-scoped token and reaches the project's private network. -- Runtime files are visible only when they are mounted into remote setup. -- Safety profile: **project-contained by design** - no direct access to your laptop, home directory, or other projects. -**Local setup** is the supervise-the-client path. -- The agent runs on your laptop. -- It can touch the same project surface, **plus whatever the agent client can reach on your laptop**. -- It reaches project services over VPN, plus everything else your laptop can already reach. -- Safety profile: **supervise the agent client** - ZCP stays project-scoped, but the agent process inherits your user. -Either path is the right choice in context. Local setup 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 exposing operations. A token with access to no projects is refused; a token with access to multiple projects is refused. The intended token form is 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). | -| 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. +The ZCP trust model starts with one rule: one ZCP process operates one Zerops project. The token decides the project boundary at startup, and ZCP refuses tokens that resolve to no project or multiple projects. +That boundary is strong on the Zerops side. It does not make the agent harmless. A project-scoped token can still deploy, change env vars, restart services, read logs, scale services, and change public access inside that project when Zerops permissions allow it. Treat it like an operations credential for one project. +## Boundary summary +| Question | Answer | +| ------------------------------------ | ------------------------------------------------------------------------------------------- | +| What project can ZCP see? | Exactly one project resolved from `ZCP_API_KEY` at startup. | +| What can ZCP change? | Whatever Zerops RBAC grants that token inside the project. | +| What is outside reach? | Other projects, organization-wide settings, billing, and any operation Zerops RBAC rejects. | +| What network can remote setup reach? | The project's private network from inside the `zcp` service. | +| What network can local setup reach? | The Zerops API directly, plus project-private services only when your laptop VPN is up. | +| Who owns the agent login? | You. The agent subscription or model API key is separate from `ZCP_API_KEY`. | +| Who owns the agent login? | You. The agent subscription or model API key is separate from `ZCP_API_KEY`. | +Zerops [RBAC](/features/rbac) remains the authority. ZCP does not bypass platform permissions; it exposes project operations to the agent only through the token it was given. +## Remote and local blast radius +Remote setup and local setup share the same project boundary, but not the same surroundings. +| Area | Remote setup | Local setup | +| -------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------- | +| Agent location | Inside the `zcp@1` service when **Include Coding Agent** is enabled | On your machine through your editor or CLI agent | +| Files visible to the agent | Workspace files and mounted runtime files | Local files and anything your client permits | +| Private service access | Project-private network without laptop VPN | `zcli vpn up ` from your machine | +| Local machine exposure | No direct access to your laptop | Agent client runs as your local user | +| Best safety posture | Keep ZCP in a development/staging project and avoid mounting unrelated files | Restrict client permissions, shell access, and filesystem scope | +| Best safety posture | Keep ZCP in a development/staging project and avoid mounting unrelated files | Restrict client permissions, shell access, and filesystem scope | +Remote setup is project-contained by design. Local setup is supervise-the-client by design. Either can be the right choice, but local setup requires more attention to your agent client's local permissions. +## Credential ownership +There are three separate credential surfaces people often mix together: +| Credential | Owner | Purpose | +| ----------------------------------- | ---------------------------- | ------------------------------------------------------ | +| `ZCP_API_KEY` | Zerops project token | Lets ZCP operate one Zerops project. | +| Agent subscription or model API key | You / the agent provider | Lets the coding agent run. Zerops does not provide it. | +| Git or CI credentials | You / your repository system | Lets finished work push to git or deploy through CI. | +| Git or CI credentials | You / your repository system | Lets finished work push to git or deploy through CI. | +Remote setup injects `ZCP_API_KEY` into the `zcp` service. Local setup reads it from the `.mcp.json` env block. In both paths, the agent account is still authenticated through the agent's own login flow. +Details: [Tokens and credentials](/zcp/security/tokens-and-project-access). +## What a project-scoped agent can do +Inside the project, the agent can perform normal operational work when the token has permission: +- discover runtime and managed services, +- create or adjust services when the task requires it, +- read and write service env vars, +- deploy app code to runtime services, +- read build logs, runtime logs, and service events, +- restart, reload, stop, start, or scale services, +- enable or disable public access, +- prepare delivery paths such as git-push or CI handoff. +This is why development and staging projects are the right place for ZCP. Production should be a separate project without a `zcp` service; see [Production boundary](/zcp/security/production-policy). ## Remote setup specifics -Remote setup is the project-contained path. A few specifics: -- **`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. -- **Project access stays inside the project.** The `zcp@1` service can read project env and reach project services so the agent can build against real dependencies without you copying credentials around. -- **Filesystem reach is narrower than network reach.** The agent reaches project services over the private network, but only sees runtime files when they are mounted into remote setup. -- **A terminal in remote setup has the same project-scoped access as the agent.** Convenient, and the reason production stays in a separate project. +- **`zcp` service is not the app.** It is the workspace and control surface. Deploys target app runtimes, not the `zcp` service. +- **The service has project-level operational reach.** A terminal in the `zcp` service can use the same project-scoped access as the agent. Review the dashboard's additional changes before deploying the service; they are what give the workspace its project-scoped operating surface. +- **Network reach is broader than file reach.** The workspace can reach project-private services, but it only sees runtime files that are mounted into it. +- **Do not hand-edit `ZCP_API_KEY`.** Remote setup gets the value from Zerops. Manual replacement can break the intended one-project boundary. ## Local setup specifics -Local setup 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; the binary doesn't merge local state across them. -- **VPN setup is outside ZCP's authority.** Bringing up the Zerops VPN requires admin or root approval on your laptop. ZCP can tell 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. -- **Local setup 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: [Set up local ZCP](/zcp/setup/local-agent-bridge). -## Audit surface -Zerops records platform-side evidence: service events, deploy/build results, runtime logs, lifecycle actions, scaling, and public-access changes. It does not record the agent's private reasoning, every shell edit, browser-helper actions, or prompt history outside your agent client. -When taking over from an agent, read evidence in this order: service-scoped events, runtime logs, deploy/verify result, then git history when delivery uses git-push. -## What ZCP refuses by design -ZCP refuses boundaries that should never be inferred: tokens that do not resolve to exactly one project, remote self-deletion, and destructive replacement before the agent has read the relevant failure evidence. Destructive confirmation details live in [Tokens and credentials](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions). +- **The agent inherits local reach.** ZCP is project-scoped, but the local agent client can read files, run commands, and use credentials allowed by your client settings. +- **Each local directory has its own config.** `.mcp.json` and `.zcp/state/` belong to one project directory. Launching from the wrong directory can connect the wrong project or no project. +- **VPN is outside ZCP authority.** Bringing up Zerops VPN needs your operating-system approval. ZCP cannot grant that for the agent. +- **`.env` files are snapshots.** They contain real project credentials and should stay out of git. +## Human confirmation gates +Most project operations do not get an extra ZCP-specific confirmation prompt. Deploys, env changes, restarts, scaling, and public-access changes are normal project operations and are audited through platform evidence. +ZCP adds hard gates where the loss is not safely reversible from the conversation: +| Operation | Gate | +| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| Service deletion | Requires explicit same-conversation approval that names the service. Remote setup also blocks deleting the `zcp` service it is running in. | +| Wholesale service replacement after failed deploy history | Requires the agent to read failure evidence first, summarize what would be replaced, and ask for explicit confirmation. | +| Wholesale service replacement after failed deploy history | Requires the agent to read failure evidence first, summarize what would be replaced, and ask for explicit confirmation. | +Approval from an old chat does not carry forward. A new conversation needs a new approval. +## Audit evidence +Zerops records platform-side evidence. It does not record the agent's private reasoning, every shell edit, browser-helper action, or prompt history outside your agent client. +When taking over from an agent, read evidence in this order: +1. service-scoped events, +2. build logs, +3. runtime logs, +4. deploy and verification output, +5. git history when delivery uses git-push. +| Surface | What it proves | What it does not prove | +| -------------- | ------------------------------------------------------------------------- | -------------------------------------------- | +| Service events | Deploy lifecycle, failures, restarts, scaling, and public-access changes. | The exact source edit that caused the event. | +| Build logs | Dependency install, build commands, compile/package failures. | Runtime request behavior after deploy. | +| Runtime logs | Start crashes, port binding, request-time app errors. | Why the build failed. | +| Verify output | Whether reachability and requested behavior passed. | That unrelated app flows work. | +| Git history | Source changes pushed during git-push delivery. | Uncommitted shell edits. | +| Git history | Source changes pushed during git-push delivery. | Uncommitted shell edits. | +Filter by service hostname when possible. Project-level timelines can include unrelated services and older failures. ## Next steps -- [Tokens and credentials](/zcp/security/tokens-and-project-access) — token scope, storage, rotation, and confirmation gates. -- [Troubleshooting](/zcp/reference/troubleshooting) — recover when the agent or session state drifts. -- [Production boundary](/zcp/security/production-policy) — keep production outside the agent loop. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and confirmation gates. +- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare blast radius before setup. +- [Production boundary](/zcp/security/production-policy) - keep production outside the agent loop. ---------------------------------------- # Zcp > Setup > Choose Workspace -Choose where the ZCP-backed agent runs. ZCP gives the agent the same project-scoped Zerops toolset in both paths, but the filesystem, network path, deploy source, and safety profile are different. -Use remote setup for the first run and project-contained isolation. Use local setup when your checkout, editor, dev server, and git credentials should stay on your machine. -## Short rule -**Remote setup** is the default first run: no local install, project-contained isolation, optional bundled agent, optional browser IDE. -**Local setup** fits when you want to keep your editor, shell, checkout, local dev server, and git credentials. -## Compare the paths -**Remote setup** -- ZCP runs inside the Zerops project in a `zcp@1` service. -- The agent runs inside that service when **Include Coding Agent** is enabled. -- Runtime files can be mounted through SSHFS. -- Managed services are reached over the project-private network. -- Deploy source, env vars, and git credentials live inside the project service. -- Safety profile: project-contained by design. -**Local setup** -- ZCP runs on your laptop as the `zcp` binary. -- The agent runs in your local editor or CLI after `zcp init`. -- App work happens in your local working directory. -- Managed services are reached from your app and shell through Zerops VPN. -- Deploy source and git credentials are your local working directory and local git setup. -- Safety profile: ZCP is project-scoped, but the agent client runs as your user. -## What changes in practice -In remote setup, a `zcp@1` service runs the `zcp` binary inside the Zerops project. Enable **Include Coding Agent** when you want the service to add the bundled agent CLI and preconfigure it to use ZCP. Enable **Cloud IDE** when you also want browser-based VS Code. Runtime files can be mounted into the service, and managed services are reachable over the project-private network. -In local setup, the agent works in your local checkout. You install the `zcp` binary and run `zcp init` in the project folder so the local agent can talk to ZCP. ZCP talks to the Zerops API directly, while your local app and shell need `zcli vpn up` to reach managed services by hostname. -After setup, ZCP reads whether the project has one runtime, a dev+stage pair, or a local checkout linked to a runtime. You usually state the outcome, not the label: dev+stage, one dev runtime, or local deploys to a named runtime. Exact labels live in [Workflow terms](/zcp/reference/agent-workflow#runtime-layouts). -Stage is a review or promotion target, not production. Production should live in a separate Zerops project; see [Production boundary](/zcp/security/production-policy). +Remote setup is the default for most ZCP work. Use local setup when the agent specifically needs to run next to local files, local data, local tools, or a local-only agent client. +Both paths use the same `zcp` binary and the same ZCP model: one Zerops project, live project state, project-scoped operations, and an agent that can finish with proof or a blocker. The choice is where the agent workspace lives. +:::tip Recommended default +**Remote setup** gives you a project-contained agent workspace with clean isolation inside the Zerops project, no local install, project-private networking, and a preconfigured Claude Code + browser VS Code environment when enabled. +::: +## The short version + Remote setup + The agent runs inside a zcp@1 service in the Zerops project. + Use it when you want the agent workspace, project-private networking, and + broad shell permissions contained inside the project. + No local install. + Claude Code and browser VS Code can be bundled. + Project-private services are reachable from the workspace. + Desktop remote editors can connect when you prefer them. + Use remote workspace → + Local setup + The agent runs from your machine. Use it when local files, local data, + your desktop editor, local git credentials, or a specific local agent + client should stay in charge. + Install zcp locally. + Run zcp init in the working directory. + Add a project-scoped token. + Use zcli vpn up for private service access. + Set up local ZCP → +Remote setup usually costs one extra workspace service. Local setup avoids that workspace service, but deploy targets and managed services still cost while running. For most teams, the decision should be about where the agent should safely and ergonomically work, not about the small workspace cost. +:::info Public preview +Remote setup and local setup are both public preview. Local setup has more moving parts and may change faster: binary install path, `.mcp.json`, and `zcp init` artifacts are still settling between releases. +::: +## What changes between the paths +| | Remote setup | Local setup | +| ---------------------- | -------------------------------------------------------------- | -------------------------------------------- | +| `zcp` process | Runs in the Zerops project in a `zcp@1` service. | Runs on your machine. | +| Agent process | Runs in the project workspace when bundled or installed there. | Runs in your local editor or CLI. | +| Files the agent edits | Workspace files and mounted runtime files. | Files on your machine. | +| Private service access | Project-private network from inside Zerops. | Zerops VPN from your machine. | +| Token storage | Platform-injected into the workspace service. | `.mcp.json` in the local project directory. | +| Git credentials | Configured inside the workspace service. | Your local git credentials. | +| Safety posture | Agent shell permissions are contained in the project service. | Agent shell permissions affect your machine. | +| Safety posture | Agent shell permissions are contained in the project service. | Agent shell permissions affect your machine. | +## Starting points +Remote setup can start from: +- **Recipe with AI Agent environment.** A guided path for a known stack. A recipe creates app services, managed services, and the `zcp@1` workspace together. +- **New project with ZCP enabled.** Start from a blank or custom project and add ZCP during project creation. +- **Existing development or staging project.** Add the `zcp` workspace next to services that already exist. Do not add ZCP to a production project; promote verified work through your release path instead. +Local setup can start from: +- **Empty local directory.** The agent creates the app structure from a product request and uses ZCP to select or create Zerops services. +- **Existing project directory.** The app code, editor setup, local data, and git credentials already live on your machine. +- **Recipe prepared for local setup.** The recipe creates the Zerops service baseline, while the agent and files stay local. +## Runtime layout is separate +Remote or local only answers where the agent and `zcp` process run. It does not decide the app runtime layout. +The project can still use: +- one mutable dev runtime, +- a dev + stage pair, +- a single app runtime, +- local files linked to a stage target. +That choice belongs to normal app work: [Build with ZCP](/zcp/workflows/build-with-zcp#choose-the-runtime-layout). +Stage is not production. Keep production in a separate Zerops project and promote work through your release path; see [Production boundary](/zcp/security/production-policy). +## Switching later +You can change setup path later without changing the ZCP project boundary. +- Start remote for evaluation, then move local when local files or tools should own the workflow. +- Start local for a repo-first project, then add remote setup when you want a project-contained agent workspace. +- Keep both paths only when your team understands which source tree is authoritative for deploys. +Before switching, make the handoff explicit: +- Commit, push, or otherwise preserve the source tree that currently owns deploys. +- Choose one deploy source for the next task: the remote workspace/mounted runtime files, or the local project directory. +- Do not edit mounted runtime files and a local repo in parallel unless you have a merge plan. +- Regenerate local `.env` snapshots after project env changes. +- Relink the local deploy target if the runtime hostname changed. ## Next steps -- [Set up remote ZCP](/zcp/setup/hosted-workspace) +- [Use remote workspace](/zcp/setup/hosted-workspace) - [Set up local ZCP](/zcp/setup/local-agent-bridge) -- [Build with ZCP](/zcp/workflows/build-with-zcp) +- [Trust model](/zcp/security/trust-model) ---------------------------------------- # Zcp > Setup > Hosted Workspace -Remote setup is a `zcp@1` service inside your Zerops project. It keeps ZCP, project-scoped credentials, private-network access, and optional agent tooling inside the project boundary. -Enable **Include Coding Agent** when you want Zerops to add the bundled agent CLI and preconfigure it to use ZCP in that service. Enable **Cloud IDE** when you also want browser-based VS Code for watching or intervening. -After provisioning, the service is ready for your first app instruction. If **Cloud IDE** is enabled, you can open the browser workspace from the dashboard; otherwise use the access method configured for the service. -Remote and local setup have the same ZCP project boundary but different work surfaces. The comparison lives in [Choose remote or local setup](/zcp/setup/choose-workspace); the broader human development modes live in [Local & Remote Development](/features/local-remote-development). -## Two paths to remote setup -Zerops provisions the service for you - no local install, no MCP config to write by hand, 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 a `zcp@1` service with **Include Coding Agent** enabled. -- **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 base result: a `zcp@1` service inside the project with `ZCP_API_KEY` injected automatically and scoped to this project. **Include Coding Agent** adds the preconfigured agent. **Cloud IDE** adds the browser editor. -## Path A — recipe + AI Agent environment -Recipes are the quickest path when their stack matches the runtime layout 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@1` service with **Include Coding Agent** enabled 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 with a bundled agent is added. 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**: +Remote setup gives you an agent workspace inside the Zerops project. +The `zcp@1` service runs the same `zcp` binary used by local setup, but the agent, terminal, and optional browser IDE run in a clean project service instead of on your laptop. That is the main advantage: Zerops can provide a low-friction agent workspace with project-private networking and broader shell permissions while keeping that blast radius inside the project. +Use this path when you want a project-contained workspace, a safer default boundary for broad agent permissions, or a preconfigured environment with Claude Code and browser VS Code. +App code still deploys to your app runtime services. The `zcp` service is the workspace and control surface; it is not the application runtime. +## What you get +- **`zcp@1` workspace service.** Runs ZCP inside the project and gives the agent project-scoped operations. +- **Platform-injected `ZCP_API_KEY`.** Zerops injects the project token into the workspace; you normally do not set it by hand. +- **Bundled Claude Code when enabled.** The **Include Coding Agent** option installs and preconfigures Claude Code. You authenticate with your own Claude subscription login or API key. More bundled agents are planned. +- **Browser VS Code when enabled.** The **Cloud IDE** option gives you a browser editor, terminal, and a place to supervise or take over the agent session. +- **Project-private networking.** The workspace can reach managed services by hostname without laptop VPN. +- **Open workspace model.** You can add other agent CLIs, private MCP servers, helper processes, dotfiles, package installs, or a derived team image. +For the local alternative, see [Set up local ZCP](/zcp/setup/local-agent-bridge). For the tradeoffs, see [Remote or local ZCP setup](/zcp/setup/choose-workspace). +## Choose a starting path +### First-time trial +Use the [Quickstart](/zcp/quickstart) when you want the guided recipe path. It covers the recipe catalog, **AI Agent** environment, Claude Code authentication, Browser VS Code, first product prompt, and proof. +After provisioning, continue with a product prompt in [Build with ZCP](/zcp/workflows/build-with-zcp). +### Recipe with AI Agent environment +Use this when you want a guided stack baseline for a new development or staging project. A recipe with an **AI Agent** environment creates app services, managed services, and the `zcp@1` workspace together. +Keep **Coding Agent** enabled when you want bundled Claude Code. Keep **Cloud IDE** enabled when you want Browser VS Code in the project. +### New project with ZCP enabled +Use this when you want a blank or custom 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. Keep **Include Coding Agent** enabled to add and preconfigure the bundled agent inside the service. Use **Configure** when you need to choose authentication or adjust Cloud IDE access; otherwise leave the defaults 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 service -Open the `zcp` service from the Zerops dashboard after it reaches running state. +2. Enter the project name, region, and tags. +3. Enable **Add Zerops Control Plane (ZCP) service**. +4. Keep **Include Coding Agent** enabled if you want bundled Claude Code. +5. Keep **Cloud IDE** enabled if you want browser VS Code. +6. Create the project. +This gives you a project with the `zcp@1` workspace. App runtimes and managed services may still be created later by normal ZCP app work. +### Existing development or staging project +Use this when a development or staging project already has runtime or managed services. Do not add `zcp` to a production project; create or use a separate development/staging project and promote verified work through the release path. +Add a `zcp` service from the project dashboard the same way you add another Zerops service. The workspace appears next to the existing services and receives project-scoped ZCP access from the platform. +The agent should still read project state before changing anything. Existing services are context, not automatic instructions for the next task. +## Open the workspace +For a returning remote workspace, open the existing `zcp` service: 1. Open the project in the [Zerops dashboard](https://app.zerops.io/). 2. Open the `zcp` service. -3. If **Cloud IDE** is enabled, open the service workspace URL. Code-server launches in a new tab. If the service is exposed on a `.zerops.app` subdomain, see [Public access](/features/access) for the routing model. -If **Cloud IDE** is disabled, remote setup can still exist as a project-contained service, but there is no browser editor to open. Use the access method configured for the service, such as SSH, or enable Cloud IDE when you want a browser workspace. -## Run the agent -When **Include Coding Agent** is enabled, the bundled agent starts already connected to this project through ZCP. In the Cloud IDE path, the terminal has the bundled agent available with `ZCP_API_KEY` in its environment. -For a connection sanity check, ask the bundled agent: -```text -List the services in this project and tell me which are runtimes versus managed dependencies. -``` -A working setup answers with the runtime services (for example `appdev`, `appstage`) and managed services (for example `db`), and says whether the project has one runtime or a dev+stage pair. -For real work, ask for the app behavior, not the Zerops procedure: -```text -Build a task board where tasks stay saved after refresh. -``` -Discovery, service setup, deploy, verification, and final reporting happen behind that app request. -If **Include Coding Agent** is disabled, the `zcp@1` service exists but no preconfigured agent is waiting inside it. Enable the option or use [local ZCP](/zcp/setup/local-agent-bridge) from your own agent client. -After the first setup pass, runtime services can be SSHFS-mounted into the service filesystem with one folder per runtime. 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 remote setup. -If you added ZCP to an existing project, the agent should inspect existing services and use them instead of creating duplicates. That is handled as part of the first product prompt; the details are explained in [Service setup behind the prompt](/zcp/workflows/create-or-adopt-services). -When Cloud IDE is enabled, the browser workspace is persistent. Close the browser tab and reopen the workspace link later; editor state, open files, and the agent session survive across reconnects as long as the `zcp` service is running. -## If connection fails -If the agent can't reach ZCP, the service may still be starting. Wait for it to reach running state in the dashboard and reload the workspace. If **Include Coding Agent** was not enabled, add it or use local ZCP from your own agent client. -At this point, the agent can receive the first app instruction. The [Quickstart](/zcp/quickstart) shows the shortest end-to-end run. -## Customize remote setup -Remote setup is a normal Zerops service, so teams can customize it like one: add tools, preload team config, or run helper processes next to the agent and Cloud IDE. Keep this separate from app runtime work; deploy app code to runtime services, not to `zcp`. -## Gotchas -- **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. If the agent targets `zcp` as the app runtime, stop and correct the target. -- **Don't set `ZCP_API_KEY` by hand in remote setup.** It's platform-injected. Manual override risks breaking the agent's access. (Local setup 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 the bundled agent. Until the service reaches running state, the workspace URL may return an empty page or error. +3. Use **Browser VS Code** when **Cloud IDE** is enabled. +4. Complete the Claude Code login or API-token flow if prompted. +After authentication, Claude Code is connected to this project's ZCP service and ready to work from the browser VS Code terminal. If the workspace was already authorized, use this same path to resume or supervise work. +First load can take a minute while the image, Cloud IDE, and bundled agent finish starting. If the page opens before the service is ready, wait until the service is running and reload. +## Use your own editor or tools +Browser VS Code is the quickest entry point, not the only one. +Editors that support remote development can connect to the workspace or runtime services over SSH, depending on how your team wants to work. Common options include VS Code Remote SSH, JetBrains Gateway, Cursor, Zed, and plain SSH. +This keeps the agent and project-private network inside Zerops while letting you use a desktop editor UI. Broader editor patterns live in [Local & Remote Development](/features/local-remote-development#native-ide-over-ssh). +You can also install another agent CLI or additional MCP servers inside the `zcp` service. The bundled Claude Code flow is a convenience, not a closed product boundary. +## Advanced customization guardrails +Tools installed inside the `zcp` service may see workspace environment variables, project-private networking, and any runtime files mounted into the workspace. Use trusted and pinned tools, and keep model credentials, git delivery tokens, external API keys, and production credentials out of the workspace unless the current task explicitly needs them. +## What belongs where +| Concern | Where it belongs | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| Agent workspace | The `zcp@1` service: agent CLI, browser VS Code, shell tools, MCP servers, helper processes, dotfiles. | +| App code deploys | Your app runtime services; not the `zcp` workspace service. | +| ZCP project token | Injected by Zerops into the `zcp` workspace. | +| Agent account | Your Claude subscription login or model API credential. Zerops wires the agent to ZCP, but the agent account remains yours. | +| Git credentials | Configured inside the workspace when the agent should commit or push from remote setup. | +| Production promotion | A separate production project and release path. | +| Production promotion | A separate production project and release path. | +## Runtime file access +Remote setup can mount runtime service filesystems into the `zcp` workspace after the first setup pass. Each mounted runtime appears as its own folder. Editing a mounted file changes the file inside that runtime service, with no upload step from your laptop. +Filesystem reach is narrower than network reach. The workspace can reach project services over the private network, but it only sees runtime files that are mounted into it. +## Make customization persistent +The `zcp@1` service is a normal Zerops service. One-off shell installs disappear when the service is rebuilt unless you make them part of the service setup. +Use these patterns: +- **Small additions:** put package installs, dotfiles, or bootstrap scripts into service init commands. +- **Team-standard workspace:** build a derived image based on `zcp@1` with required tools already present. +- **Helper processes:** run project-local helpers next to the agent and editor workspace when your team needs private integrations. +Keep app runtime build steps with the runtime services. The remote workspace carries the agent, tools, and project access; runtime services own app builds and deploys. +## Next steps +- [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - how `ZCP_API_KEY`, git credentials, and CI secrets differ. +- [Production boundary](/zcp/security/production-policy) - why production belongs in a separate project. ---------------------------------------- # Zcp > Setup > Local Agent Bridge -Local setup runs the `zcp` binary on your laptop, initialized in one project directory. The agent edits your checkout, your tools run the dev server, and ZCP deploys from your working directory to the linked Zerops runtime. -`zcp init` writes the local MCP configuration for that folder, so a compatible local agent client can talk to ZCP as the `zerops` MCP server. With `zcli vpn up `, your laptop also joins the project's private network - the same network your services use. -Local setup is newer than remote setup. Expect install details and generated project files to move between releases. -Use this path when you already have a comfortable local setup - your editor, your shell, your dev server - and want a coding agent that can drive Zerops from the same machine. If you're evaluating ZCP for the first time, [remote setup](/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. ZCP is what you add when you also want a coding agent operating the project from the same machine: discovering services, generating local env snapshots, deploying, reading logs, verifying. -Security note: ZCP itself stays project-scoped, but the agent client runs as your local user. Set your client permissions accordingly. +Local setup runs ZCP on your machine. +Use it when the agent should work next to local files, local data, your desktop editor, your terminal tools, and your local git credentials. ZCP still scopes Zerops operations to one project, but the agent client itself runs as your local user. Your local agent permissions and filesystem allowlists matter. +[Remote setup](/zcp/setup/hosted-workspace) is the safer default when you do not need local files or tools. Use local setup when the local workflow is the point. +:::warning Public preview +Local setup has more moving parts than remote setup and may change faster. The binary install path, `.mcp.json` shape, and files written by `zcp init` are still settling between releases. +::: +## Choose a local starting point +Pick the folder that should own app work: +- **Empty local directory.** Start with no app code yet. The agent can create the app structure and use ZCP to select or create Zerops services. +- **Existing project directory.** Use this when app code, local data, editor setup, test fixtures, and git credentials already live on your machine. +- **Recipe prepared for local setup.** Use a recipe to create the Zerops service baseline, then run the agent locally from the directory that should own source changes. +After that choice, the mechanics are the same: install `zcp`, run `zcp init`, add a project-scoped token, start VPN when private service access is needed, and link a deploy target when the agent should deploy. +## What local setup gives the agent +- **Local files as source.** The agent edits the directory on your machine, and deploys use that working directory. +- **Your editor and terminal.** Framework CLIs, test runners, local data, and local feedback stay under your normal tools. +- **Your git credentials.** Pushes use your local git CLI, SSH agent, or credential helper. +- **Project-scoped Zerops operations.** ZCP lets the agent discover services, generate env snapshots, deploy to linked runtimes, read logs, and verify the project. +- **Private service access through VPN.** Your local app and shell reach project services through `zcli vpn up`. +Security note: local ZCP cannot protect your laptop from the agent client. Configure your local agent approvals the same way you would for any coding agent running on your machine. ## 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 compatible local agent client installed and logged in. ZCP doesn't see the agent client's subscription or login credentials. -## Get a project-scoped Zerops token -ZCP needs a Zerops API token scoped to **one project**. Multi-project tokens, including account-wide full-access tokens, are refused at startup. Generate one in the Zerops dashboard, then keep the value handy for the local MCP configuration step. Full walkthrough, rejection rules, and rotation: [Tokens and credentials](/zcp/security/tokens-and-project-access). -## Install `zcp` +- A Zerops project. Create one from the [Zerops dashboard](https://app.zerops.io/dashboard/project-add), from a [recipe](https://app.zerops.io/recipes), or use an existing development/staging project. +- [zCLI](/references/cli) installed and authenticated on your machine. +- A compatible local agent client installed and logged in. +- A **project-scoped Zerops token** for exactly one project. Multi-project tokens are refused at startup. +- A local directory where the agent should run. +You do not need ZCP just to develop locally against Zerops services as a human. `zcli vpn up` plus your editor is enough for that. Add ZCP when a local coding agent should also understand and operate the Zerops project. +## 1. Get a project-scoped token +ZCP needs a Zerops API token scoped to exactly one project. For normal agent work, use a single-project token with full access to that project. Read-only tokens can authenticate but fail on deploys, env changes, lifecycle actions, and other mutations. +Token generation, rejected shapes, and rotation are covered in [Tokens and credentials](/zcp/security/tokens-and-project-access). Keep the token value available for the `.mcp.json` step below. +## 2. Install `zcp` ```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. -## Run `zcp init` in your project folder -In a terminal in your project's working directory: +The installer downloads the latest release for your platform into `~/.local/bin`, or `/usr/local/bin` when run as root. Verify the install: +```bash +zcp version +``` +If your shell cannot find `zcp`, add the install directory to `PATH` and reload the shell. +## 3. Run `zcp init` +From the local directory the agent should operate: ```bash zcp init ``` -`zcp init` writes the local MCP configuration and agent instructions for this project. Re-running it may refresh generated config; after rerunning it, confirm `.mcp.json` still contains `ZCP_API_KEY` before launching the agent. -## Configure `zcp` as the local MCP server -`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 your agent client from the 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--remote-vs-local). -The agent client starts and lists the MCP servers it found. The `zerops` server should be in the list; that is the local ZCP connection. -### Sanity check +`zcp init` writes the local MCP configuration and agent instructions for this project directory. +Current generated artifacts: +- `.mcp.json` - MCP configuration your local agent client reads to discover ZCP. +- `CLAUDE.md` - agent instructions for operating ZCP in this project. +- `.claude/settings.local.json` - Claude Code per-project settings when that client is used. +- `~/.config/zerops/aliases` plus a shell-rc sourcing line - helper aliases for launching the agent from this project. +- `.zcp/state/` - created later when ZCP first writes local project state. +Re-running `zcp init` may refresh generated config. `CLAUDE.md` preserves edits outside managed markers, but `.mcp.json` is regenerated from the token-less template. If you rerun it, re-check the `ZCP_API_KEY` env block before launching the agent again. +## 4. Add `ZCP_API_KEY` +`zcp init` writes a token-less `.mcp.json`. Add the project token under the `env` block: +```json +{ + "mcpServers": { + "zerops": { + "command": "zcp", + "args": ["serve"], + "env": { + "ZCP_API_KEY": "" + } + } + } +} +``` +Add `.mcp.json` to `.gitignore`. It contains a live project credential and should not leave the machine. Each project directory should have its own `.mcp.json` and its own project token. +The server name `zerops` is intentional. Do not rename that key unless your agent client explicitly requires a different name and you understand the resulting prompt/instruction changes. +## 5. Launch the agent from the project root +Start your local agent client from the same directory that contains `.mcp.json`. The client should list `zerops` as an available MCP server. +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 the agent client from the wrong directory. -## Connect private services with `zcli vpn up` -Your laptop reaches managed services (databases, caches, storage) through the Zerops project [VPN](/references/networking/vpn). ZCP 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: +A working connection answers with the project's runtime and managed services. If the agent cannot reach ZCP, check the launch directory, token scope, and whether the client loaded `.mcp.json`. +## 6. Bring up VPN when private services are needed +ZCP can talk to the Zerops API without VPN. Your local app, shell, tests, database clients, and framework commands need VPN to reach project-private service hostnames such as `db` or `cache`. ```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. -## Local env bridge -When a local app needs project credentials, ask for the product change and let ZCP generate the local env bridge as part of that work. For a standalone sanity check, you can ask: +VPN setup needs admin or root approval on macOS and Linux. ZCP can tell the agent which command is needed, but it cannot approve or start the VPN for you. +After the tunnel is up, project service hostnames resolve from your machine. Laptop sleep, network changes, and idle time can drop the tunnel; run the command again when local service connections start failing. +## 7. Generate local env when needed +When the local app needs project credentials, ask the agent for the app work and let ZCP generate the env bridge as part of that work. For a standalone check: ```text Use ZCP to generate a .env file for my local app. ``` -ZCP resolves the project env references your app would see when deployed and writes a `.env` snapshot in your working directory. Your local app uses the same hostnames and credentials, reached over VPN. Regenerate after changing project env variables. -## Linked deploy target -Local setup needs one Zerops runtime linked as the deploy target, often a stage runtime, 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. Ask the agent to link a runtime by hostname when ready. -With a linked runtime, ZCP can deploy from your working directory. Without one, it can still inspect the project and generate env snapshots, but deploys need a target runtime first. -## 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. Remote ZCP can give the agent SSHFS access; local ZCP 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; the agent client 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 the agent client both expect it. -- **The `.env` is a snapshot.** Regenerate after changing project env variables, not before. +ZCP resolves the env references your app would see when deployed and writes a `.env` snapshot in the working directory. The file is a snapshot, not a live sync. Regenerate after changing project env variables. +Keep `.env` out of git. It contains real project connection values. +## 8. Link a deploy target +Local setup needs a Zerops runtime when the agent should deploy from your working directory. A stage runtime is the usual target because it lets local work prove itself before any production promotion path. +If the project has exactly one runtime, ZCP can use it automatically. If multiple runtimes exist, the agent should ask which one to link. Answer by hostname, for example: +```text +Link this local directory to appstage for deploys. +``` +Without a linked runtime, ZCP can still inspect services and generate env snapshots, but deploys from your local directory need a target first. +## What stays outside ZCP +ZCP does not replace your local feedback loop. Vite, Valet, Docker Compose, your IDE runner, framework CLIs, local fixtures, and test data stay under your normal local tooling. +ZCP does not mount Zerops runtime filesystems on your laptop. Remote setup can mount runtime files into the workspace; local setup works from files on your machine. If you need to inspect runtime files from your machine, use [SSH](/references/networking/ssh) directly. +ZCP does not own your git credentials. In local setup, your local git CLI, SSH agent, or credential helper handles pushes. +## Local setup checks +- **Launch directory matters.** Start the agent from the folder that contains `.mcp.json`; otherwise it may connect to no ZCP server or the wrong project. +- **Multi-project tokens are rejected.** Use a token scoped to exactly one project. +- **`zcp init` can remove the token from `.mcp.json`.** Re-add `ZCP_API_KEY` after rerunning init. +- **VPN is separate from ZCP auth.** The agent may reach ZCP while your app still cannot reach `db` because VPN is down. +- **`.env` is a credential snapshot.** Regenerate after project env changes and keep it out of git. - **Production stays out of this loop.** Local ZCP is for development and staging projects. ## Next steps -- [Deploy, verify, and fix](/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 remote or local setup](/zcp/setup/choose-workspace) - remote vs local and runtime layouts. - ----------------------------------------- - -# Zcp > Workflows > Build And Verify App - -After the target runtime and dependencies are known, the agent can change code and `zerops.yaml`, deploy the runtime, fix failures from evidence, and verify the result. -A ZCP task is done only when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the working URL or a concrete blocker. -## What the agent does -ZCP treats deploy as diagnostics and verification as the completion gate. The user does not have to script this sequence; it is the expected path behind an app prompt. -- **Runtime scope.** The agent identifies which runtime changed. -- **Code and `zerops.yaml`.** The agent changes app files and runtime config where needed. -- **First deploy.** The first verified runtime deploy goes through ZCP. -- **Runtime reachability.** The agent checks that the runtime answers on the real platform path. -- **Requested behavior.** The agent proves the endpoint, URL, UI state, row, job result, or other acceptance evidence. -- **Evidence-based fix.** If either verification layer fails, the agent reads new logs/events/check output, fixes the cause, and redeploys. -A green build or running runtime is not done until the requested endpoint, UI, or state is checked on the real URL. Retries should use new evidence, not repeat the same deploy. -## Runtime scope is a precondition -Before editing, the agent should make the runtime service visible: `appdev`, `appstage`, `app`, or the linked runtime in a local setup. Managed services such as databases and caches are dependencies, not deploy targets. -If the agent starts writing files before it knows the runtime scope, treat that as a correction signal: it should read ZCP status and identify the target before continuing. -## Deploy -The first deploy uses the direct ZCP deploy path. Delivery choices such as git-push or external handoff apply after a verified deploy exists. -Deploy failures point to different evidence: -| Failure point | Read first | -|---|---| -| Build failed | Build logs and `zerops.yaml` build steps | -| Runtime started then crashed | Runtime logs and start command | -| App is up but route fails | Runtime logs at request time and behavior check output | -| Local app cannot reach managed services | VPN state and generated `.env` values | -| Local app cannot reach managed services | VPN state and generated `.env` values | -## Verify -Verification has two layers: -| Layer | What it proves | -|---|---| -| Platform reachability | The service is running, recent error logs are checked, and HTTP runtime services answer a probe when eligible. | -| Requested behavior | The thing you asked for works: endpoint body, UI state, database row, job result, or stage URL. | -| Requested behavior | The thing you asked for works: endpoint body, UI state, database row, job result, or stage URL. | -Both layers must pass before the agent reports completion. -## Fix or stop -The agent should name the failure point in plain language, read fresh logs/events/check output, fix the indicated cause, and redeploy. A repeated retry with no new evidence is not progress. -Stop and ask for a decision when scope is ambiguous, credentials are missing, destructive recovery is needed, or several evidence-based retries hit the same failure. -## Review and take over -The platform records deploys, events, runtime logs, and lifecycle actions. It does not record every agent thought, browser click, or shell edit. When taking over, ask for ZCP status first, then read service-scoped events, logs, deploy/verify results, and git history if delivery uses git-push. -## Gotchas -1. **Deploy success is not verify success.** A green build with a broken feature is not complete. -2. **Stage is explicit.** In dev+stage projects, the agent should say when stage is included before changing or verifying it. -3. **Secrets belong in env vars.** If the app needs a third-party API key, the agent should ask for an env-var path, not put secrets in source. +- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare setup paths. +- [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and destructive confirmations. ---------------------------------------- # Zcp > Workflows > Build With Zcp -Start with what the app should do, not with tool calls or workflow names. +Build with ZCP starts from product intent, not from an operations checklist. ```text -Build a task board where tasks stay saved after refresh. -``` -Behind that prompt, the agent should inspect the project, choose a target runtime, make the app change, and use real deploy evidence until the requested behavior is proven. ZCP supplies the project knowledge and tool access needed for that loop: live services and env state, Zerops guidance, deploys, logs, verification, and guarded infrastructure changes. -Do not make the prompt longer to restate completion expectations such as deploy, verification, or returning the URL. The words you add should change the app, the stack, the runtime layout, the acceptance criteria, or the delivery path. -You also do not need to paste an infrastructure inventory, env wiring plan, or log summary into the prompt. ZCP makes those available to the agent from the live project. -## What happens after your prompt -The agent should make this path visible as it works: -**Project discovery.** Read what exists now: runtime services, managed services, env vars, logs, recent events, and current work state. -**Service setup.** Use existing services when they fit, or create the missing runtime and managed dependencies before app code starts. -**Runtime scope.** Identify the app runtime that will receive code, such as `appdev`, `appstage`, `app`, or a linked local deploy target. The `zcp` service is never the app target. -**Code, deploy, verify, fix.** Change app files and `zerops.yaml`, deploy through Zerops, verify reachability and requested behavior, read evidence on failure, and retry from the cause. -**Delivery after proof.** After a verified deploy exists, keep direct deploy, push to git, or use external handoff to CI or a human. -Advanced export/reuse path: [Package a running service](/zcp/workflows/package-running-service). +Build a task board. +Tasks should stay saved after refresh. +``` +Behind that prompt, ZCP gives the agent a development surface for one Zerops project: settle the runtime layout when it is unclear, work from live project context and Zerops-specific guidance, then close according to the delivery preference after proof. +Prompt for the outcome, constraints, acceptance criteria, runtime layout, and delivery preference when they matter. ZCP treats deploy, verification, evidence reading, and proof or blocker reporting as part of completion. +## The lifecycle + Product prompt + Build a task board... + 1. Runtime layout + Settle the app runtime and dependencies. + Existing services + Missing services + Layout choice + 2. Development context + Give code-facing work live project state. + Live project state + Relevant knowledge + Deploy + verify + 3. Delivery preference + Close app work after proof or a clear blocker. + Proof first + Keep direct deploy + Git, CI, or handoff +The important user-facing controls are the runtime layout you want, the product or code change you ask for, and the delivery preference that should apply after the work is proven. You do not run the flows by hand; ZCP uses them to give the agent the right context and done criteria. +## 1. Settle the runtime layout {#choose-the-runtime-layout} +When the project does not yet have a clear app runtime and dependency set, ZCP prepares the project layout before app feature work starts. It reads current project state, uses existing services when they fit, creates missing app runtimes or managed services when needed, and stops when the agent knows where app code belongs. +The user-facing choice that most affects this first step is the runtime layout. You can let ZCP infer it from the project, or you can name the layout in the prompt when it matters. +| Runtime layout | Use when | What to tell the agent | +| ------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------- | +| **Dev** | You want one mutable runtime for fast iteration, experiments, or early app work. | `Build a small Node.js API on dev.` | +| **Dev + stage** | You want a development runtime plus a separate runtime for review or promotion rehearsal. | `Build a Node.js API with PostgreSQL on dev+stage.` | +| **Stage / linked target** | You work from local files or a single target runtime and want ZCP to use that target. | `Use appstage as the deploy target for this local app.` | +| **Stage / linked target** | You work from local files or a single target runtime and want ZCP to use that target. | `Use appstage as the deploy target for this local app.` | +If you do not specify a layout, the agent should infer from the existing project and ask only when the choice changes cost, runtime layout, stage scope, credentials, or a destructive action. +The layout is about app runtimes, not where ZCP itself runs. Remote setup can use any of these layouts from the `zcp@1` workspace. Local setup usually works from local files into a linked stage or app runtime. +Managed services such as PostgreSQL, Valkey, queues, search, storage, or mail are dependencies. ZCP gives the agent their state and wiring patterns, but app code deploys to runtime services. +## 2. Develop with live project context {#develop-with-live-project-context} +Any instruction that implies interaction with code starts or continues app development: build, inspect, change, fix, continue, deploy, or verify app work. ZCP wraps that work with the project context the agent needs, but it does not replace the normal coding conversation between you and the agent. +Inside app development, the agent can still read files, make implementation choices, run tools, and follow your product instructions. ZCP matters around that work: it supplies current project state, relevant Zerops knowledge for the services in the project, scoped operations, deploy evidence, verification checks, and recovery rules. +| What the agent gets from ZCP | Why it matters during development | +| ---------------------------- | ------------------------------------------------------------------------------------------------------- | +| Live project state | Services, runtime layout, env-var keys, Zerops references, events, deploys, logs, and saved work state. | +| Zerops knowledge | Runtime and managed-service wiring, `zerops.yaml`, env refs, ports, public access, and deploy behavior. | +| Project-scoped tools | Operations for service setup, env vars, deploys, logs, lifecycle, verification, and recovery. | +| Workflow instructions | When to use existing services, when to create missing ones, when to read evidence, and when to ask. | +| Workflow instructions | When to use existing services, when to create missing ones, when to read evidence, and when to ask. | +That means you usually do not paste an infrastructure inventory, env wiring plan, or log summary into the chat. A short product prompt can be enough because the agent can ask ZCP what exists now. +What you control here is the product request, stack preference, acceptance criteria, and any instruction to inspect or continue existing work. ZCP gives the agent the deploy, verification, evidence reading, and recovery loop behind that request. +Expect the agent to stop for external credentials, destructive actions, cost-affecting infrastructure, ambiguous runtime or stage choices, and product decisions it cannot infer. +| Development input | Use when | What to tell the agent | +| ----------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------- | +| **Product behavior** | The feature or app outcome matters more than the exact stack. | `Build a task board where tasks stay saved after refresh.` | +| **Stack preference** | You want a specific runtime, framework, or managed service. | `Use Laravel and the existing PostgreSQL service.` | +| **Acceptance criteria** | A specific behavior must be true before the task is done. | `A user can create a task, refresh the page, and still see it.` | +| **Runtime layout** | The target layout matters for the work or review path. | `Build a Node.js API with PostgreSQL on dev+stage.` | +| **Existing work** | A previous ZCP session stopped or the project already exists. | `Read ZCP status first, then continue the interrupted task board work.` | +| **Existing work** | A previous ZCP session stopped or the project already exists. | `Read ZCP status first, then continue the interrupted task board work.` | +## 3. Choose delivery preference {#choose-delivery-after-proof} +Delivery preference is how app work closes after there is a verified result. You can include it in the original prompt or set it later. +The first functional deploy is still direct so the agent can prove the app actually runs. Delivery preference decides what happens after that proof and how later ZCP sessions should finish similar work. +| Delivery preference | What it means | What to tell the agent | +| ---------------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| **Keep direct deploy** | ZCP keeps deploying directly to the target runtime for fast dev/stage iteration. | `Keep direct deploy for now.` | +| **Push to git** | The agent commits and pushes working changes to the configured repository. | `When the app works, push changes to git@github.com:my-org/task-board.git.` | +| **CI / handoff** | A repository integration, GitHub Actions workflow, release process, or human owns the next deploy. | `Set up GitHub Actions delivery for future deploys after the app works.` | +| **CI / handoff** | A repository integration, GitHub Actions workflow, release process, or human owns the next deploy. | `Set up GitHub Actions delivery for future deploys after the app works.` | +ZCP records the delivery choice so later work can follow the same path without re-explaining the handoff every time. Git push credentials, CI secrets, and production credentials are separate from `ZCP_API_KEY`; see [Tokens and credentials](/zcp/security/tokens-and-project-access). +Production should still be a separate Zerops project. ZCP can prepare the handoff from dev or stage, but production promotion belongs to your release path; see [Production boundary](/zcp/security/production-policy). ## What the final answer should contain For a completed app task, the agent should report: -- which runtime service changed, -- which deploy passed, -- which URL, endpoint, or UI state proved the requested behavior, -- any env vars, managed services, or delivery settings touched, -- the delivery choice if one was set. -If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the next decision needed from you. - ----------------------------------------- - -# Zcp > Workflows > Create Or Adopt Services - -When an app prompt needs infrastructure, ZCP should discover what already exists before creating runtimes or managed services. This usually happens because you asked for a product outcome, not because you explicitly asked for infrastructure. -For example, `Build a task board where tasks stay saved after refresh.` may require an app runtime and database. `Use the existing Laravel services` tells the agent to reuse what is already there instead of creating duplicates. -Behind that prompt, the agent should answer: -- Which runtime service will hold app code? -- Which managed services does the app depend on? -- Do those services already exist, or should the agent create them? -- Are they running and visible to ZCP? -This part does **not** write application code, create `zerops.yaml`, run the first deploy, or verify behavior. Those belong to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). -## Starting situations -| Situation | What should happen | -|---|---| -| Runtime services already exist | Use them. Identify runtimes and managed dependencies. Do not recreate or rename services just to begin. | -| Project has only the `zcp` service | Treat it as empty from the app point of view. Create or import the requested service layout. | -| Project is empty and request matches a known stack | A recipe may be a good starting base, but the starter still has to be changed into the requested app. | -| Project is empty and request needs a custom layout | Create a custom service plan before app work starts. | -| Setup was interrupted | Resume from current ZCP status instead of starting over. | -| Setup was interrupted | Resume from current ZCP status instead of starting over. | -The `zcp` service is the ZCP setup, not the application runtime target. -## Prompt examples -Product prompts can imply the service setup; they do not need to spell it out: -```text -Build a Node.js API with PostgreSQL. Use the existing dev+stage pair if this project has one. -``` -```text -Use the existing Laravel services and build a small notes app. -``` -```text -Add saved tasks to this task board. -``` -The agent should explain whether it used existing services or created new ones, then move into app work when infrastructure is known. -## Done state -Service setup is done when the app runtime and managed dependencies are known, any new services are visible, and the agent is ready to start app code, `zerops.yaml`, deploy, and verification work. -When files, framework setup, deploys, logs, or behavior checks appear, the work has moved to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). -## Failure signals -| Symptom | What to do | -|---|---| -| Agent targets `zcp` as the app | Correct the target to the runtime service. | -| Agent recreates existing runtimes | Ask it to inspect current services and use what exists. | -| Agent writes app files during service setup | Stop and finish infrastructure first, or move explicitly to app work. | -| Service creation fails | Treat it as a hard stop until the failure is understood. | -| Service creation fails | Treat it as a hard stop until the failure is understood. | - ----------------------------------------- - -# Zcp > Workflows > Delivery Handoff - -Delivery is decided after the app works. You can include delivery intent in the original product prompt, but ZCP applies it only after a verified deploy exists. The first deploy uses ZCP's direct deploy path; the delivery choice records how future changes should be delivered. -## Three delivery choices -**Keep direct deploy** for fast iteration, solo projects, and demos. The agent keeps deploying through ZCP directly. -**Push to git** when you want commits, reviews, or a repository-driven build. The agent commits and pushes to a configured remote; a build integration may run afterward. -**External handoff** when your CI, release process, or a human owns delivery. ZCP records what happened but does not initiate future deploys. -You can ask in plain language: -```text -After the app verifies, set up git-push delivery to git@github.com:my-org/task-dashboard.git. -``` -```text -Keep ZCP deploying directly for now. -``` -```text -My CI takes over after this verified deploy. -``` -## Push to git -Git-push needs committed code, a remote URL, and credentials that can push. -Remote and local setup differ: -- Remote setup uses credentials configured inside the `zcp` service, commonly a fine-grained git token. -- Local setup uses your local git credentials and working directory. -A push is not proof that the app works. If a webhook or GitHub Actions build runs from the push, the agent should observe the build result and verify the app afterward. -## Change later -Delivery choice is not permanent. You can move from direct deploy to git-push later, add a build integration later, or switch to external handoff for a release that needs human control. -## Gotchas -1. **Delivery choice is not a hidden deploy button.** It records the future path after the app already works. -2. **External handoff is not a deploy by itself.** It means a human or external system owns delivery. -3. **GitHub Actions uses a Zerops token.** The Actions secret for `zcli` deploys is `ZEROPS_TOKEN`, not your GitHub token. +- the runtime service it changed, +- the deploy or verification target, +- the URL, endpoint, UI state, job result, or stored data that proves the requested behavior, +- managed services, env vars, or delivery settings it touched, +- the delivery preference that now applies. +If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the decision or credential needed from you. +Advanced reuse path: [Package a running service](/zcp/workflows/package-running-service) turns a verified runtime into a re-importable bundle. It is not the normal lifecycle for the next app change. +## Next steps ---------------------------------------- # Zcp > Workflows > Package Running Service -Use packaging when a deployed runtime should become a reusable import bundle. This is an advanced handoff or reuse workflow, not the normal way to deploy the next change. 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 layout into a fresh Zerops project. +Use packaging when a deployed runtime should become a reusable import bundle. This is an advanced handoff or reuse workflow, not the normal way to deploy the next app change. +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. The import file points back to the same source repo through `buildFromGit:`, so a fresh Zerops project can rebuild the app from git instead of carrying source code inside YAML. ## 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 a re-importable snapshot you can paste into a fresh project later. - 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 the next app change deployed. For that, ask for the app result; direct deploy is the expected ZCP path behind the request. - You want to deploy the next change to an existing production project. Keep production in its own Zerops project and operate it through Zerops UI, the API, or CI. -Packaging does not keep a long-running session. If interrupted, the agent reads live state and re-runs the export flow; you only decide when the dev or stage runtime, or env classification, is ambiguous. +Packaging does not keep a long-running session. If interrupted, the agent reads live state and prepares the bundle again. You only need to decide when the runtime choice or env-var classification is ambiguous. ## 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. +Packaging captures **one** runtime. If your project has a dev runtime and a separate stage runtime, the agent asks which one to package. They may carry different env values, start commands, or `setup:` blocks. For single-runtime projects, the choice is implied. +Managed services (`db`, `redis`, etc.) come along automatically as dependencies; you do not pick them one by one. 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. +The agent handles the packaging work; you only step in if it asks which runtime to package or how to classify a value. ## Classify env vars (secrets, project-scoped values, public values) -Once the runtime is chosen, 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. | -| `plain-config` | Literal runtime config (`LOG_LEVEL`, `NODE_ENV`, feature flags) | The literal value, verbatim. | -ZCP does not guess these buckets from key names alone. The agent should classify them from source code: if the value is read by a Stripe SDK call it is `external-secret`; if it appears as `${db_*}` from a managed service it is `infrastructure`. The packaging response carries env *keys* and redacts values; the agent fetches values only when it needs them. +Once the runtime is chosen, ZCP prepares 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. | +| `plain-config` | Literal runtime config (`LOG_LEVEL`, `NODE_ENV`, feature flags) | The literal value, verbatim. | +ZCP does not guess these buckets from key names alone. The agent should classify them from source code: if the value is read by a Stripe SDK call it is `external-secret`; if it appears as `${db_*}` from a managed service it is `infrastructure`. The packaging response carries env _keys_ and redacts values; the agent fetches values only 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. +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 #zeropsPreprocessor=on -# zerops-project-import.yaml — paste into a fresh Zerops project +# zerops-project-import.yaml - paste into a fresh Zerops project project: name: demo envVariables: @@ -22477,7 +22866,7 @@ services: priority: 10 ``` ```yaml -# zerops.yaml — verbatim copy from the running runtime +# zerops.yaml - verbatim copy from the running runtime zerops: - setup: appdev build: @@ -22499,26 +22888,26 @@ zerops: DATABASE_URL: postgres://${db_user}:${db_password}@${db_hostname}:${db_port}/${db_dbName} ``` A few things worth noticing: -- The first line is the [Zerops preprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `` directives (here `)>` for `APP_KEY`), `#zeropsPreprocessor=on` must be 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. +- The first line is the [Zerops preprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `` directives (here `)>` for `APP_KEY`), `#zeropsPreprocessor=on` must be line 1. Without it, the platform skips directive expansion at re-import and the literal string lands in the env var. ZCP adds this 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). -Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen handoff path expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through the normal [handoff path](/zcp/workflows/delivery-handoff). +Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen delivery preference expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through the normal [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). ## 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 | -| Production config | Keep production in a separate Zerops project | -## Gotchas -1. **Packaging is not the delivery path by itself.** It produces the bundle. Commit and push happen through your chosen [handoff path](/zcp/workflows/delivery-handoff). +| 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 | +| Production config | Keep production in a separate Zerops project | +## Packaging checks +1. **Packaging is not the delivery path by itself.** It produces the bundle. Commit and push happen through your chosen [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). 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. -- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) — the verified runtime this packages. -- [How ZCP works](/zcp/concept/how-it-works) — why packaging stays stateless. +- [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) - choose how the bundle's two files ship. +- [How ZCP works](/zcp/concept/how-it-works#verification-has-two-layers) - the verified runtime this packages. +- [How ZCP works](/zcp/concept/how-it-works) - why packaging stays stateless. ---------------------------------------- diff --git a/apps/docs/static/llms.txt b/apps/docs/static/llms.txt index 402547314..039faa2d3 100644 --- a/apps/docs/static/llms.txt +++ b/apps/docs/static/llms.txt @@ -337,10 +337,7 @@ - [Zcp > Setup > Choose Workspace](https://docs.zerops.io/zcp/setup/choose-workspace.md) - [Zcp > Setup > Hosted Workspace](https://docs.zerops.io/zcp/setup/hosted-workspace.md) - [Zcp > Setup > Local Agent Bridge](https://docs.zerops.io/zcp/setup/local-agent-bridge.md) -- [Zcp > Workflows > Build And Verify App](https://docs.zerops.io/zcp/workflows/build-and-verify-app.md) - [Zcp > Workflows > Build With Zcp](https://docs.zerops.io/zcp/workflows/build-with-zcp.md) -- [Zcp > Workflows > Create Or Adopt Services](https://docs.zerops.io/zcp/workflows/create-or-adopt-services.md) -- [Zcp > Workflows > Delivery Handoff](https://docs.zerops.io/zcp/workflows/delivery-handoff.md) - [Zcp > Workflows > Package Running Service](https://docs.zerops.io/zcp/workflows/package-running-service.md) - [Zerops Yaml > Base List](https://docs.zerops.io/zerops-yaml/base-list.md) - [Zerops Yaml > Cron](https://docs.zerops.io/zerops-yaml/cron.md) diff --git a/zerops-llm-script.ts b/zerops-llm-script.ts index 7127f4ea6..6a19eabfa 100644 --- a/zerops-llm-script.ts +++ b/zerops-llm-script.ts @@ -3,6 +3,7 @@ import * as path from 'node:path' import * as globModule from 'glob' const frontmatterRegex = /^\n*---(\n.+)*?\n---\n/ +const frontmatterBlockRegex = /^\s*---\n([\s\S]*?)\n---\n/ const mdxComponentRegex = /<[^>]+>/g const imageRegex = /!\[.*?\]\(.*?\)/g const importRegex = /^import\s+.*?from\s+['"].*?['"];?/gm @@ -85,6 +86,20 @@ function cleanMarkdownContent(content: string): string { return processedLines.join('\n') } +function hasFrontmatterFlag(content: string, flag: string): boolean { + const frontmatter = content.match(frontmatterBlockRegex)?.[1] ?? '' + const flagRegex = new RegExp(`^${flag}:\\s*true\\s*$`, 'm') + return flagRegex.test(frontmatter) +} + +function shouldIncludeInLLMDocs(file: string): boolean { + const fileContent = fs.readFileSync(path.resolve(contentDir, file), 'utf-8') + return ( + !hasFrontmatterFlag(fileContent, 'draft') && + !hasFrontmatterFlag(fileContent, 'unlisted') + ) +} + async function generateContent( files: string[], contentDir: string @@ -122,7 +137,9 @@ async function generateLLMDocs() { const outputListFile = path.resolve(publicDir, 'llms.txt') - const optionalFiles = globModule.sync('**/*.mdx', { cwd: contentDir }) + const optionalFiles = globModule + .sync('**/*.mdx', { cwd: contentDir }) + .filter(shouldIncludeInLLMDocs) const optionals: string[] = [] @@ -153,7 +170,9 @@ async function generateLLMDocs() { console.log(`< Output '${outputListFile}' `) const outputFullFile = path.resolve(publicDir, 'llms-full.txt') - const files = globModule.sync('**/*.mdx', { cwd: contentDir }) + const files = globModule + .sync('**/*.mdx', { cwd: contentDir }) + .filter(shouldIncludeInLLMDocs) const fullContent = await generateContent( files, From 4e6fa9c45a9e7febcb8b477f086124931fbe3f3a Mon Sep 17 00:00:00 2001 From: krls2020 Date: Sun, 10 May 2026 22:58:51 +0200 Subject: [PATCH 13/24] Refine ZCP docs copy and setup framing --- apps/docs/content/features/coding-agents.mdx | 16 ++-- .../features/local-remote-development.mdx | 10 ++- .../docs/content/zcp/concept/how-it-works.mdx | 60 +++++++------- apps/docs/content/zcp/glossary.mdx | 24 +++--- apps/docs/content/zcp/overview.mdx | 38 ++++----- apps/docs/content/zcp/quickstart.mdx | 42 +++++----- .../content/zcp/reference/agent-workflow.mdx | 24 +++--- apps/docs/content/zcp/reference/index.mdx | 8 +- .../content/zcp/reference/mcp-operations.mdx | 68 +++++++++++++--- .../content/zcp/reference/troubleshooting.mdx | 28 +++---- .../zcp/security/production-policy.mdx | 40 +++++----- .../security/tokens-and-project-access.mdx | 40 +++++----- .../docs/content/zcp/security/trust-model.mdx | 32 ++++---- .../content/zcp/setup/choose-workspace.mdx | 63 +++++++-------- .../content/zcp/setup/hosted-workspace.mdx | 60 +++++++------- .../content/zcp/setup/local-agent-bridge.mdx | 80 +++++++++++-------- .../zcp/workflows/build-and-verify-app.mdx | 4 +- .../content/zcp/workflows/build-with-zcp.mdx | 53 ++++++------ .../workflows/create-or-adopt-services.mdx | 4 +- .../zcp/workflows/delivery-handoff.mdx | 4 +- .../zcp/workflows/package-running-service.mdx | 12 +-- apps/docs/sidebars.js | 4 +- 22 files changed, 392 insertions(+), 322 deletions(-) diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx index 1e482d9cc..aca964f99 100644 --- a/apps/docs/content/features/coding-agents.mdx +++ b/apps/docs/content/features/coding-agents.mdx @@ -3,10 +3,12 @@ title: ZCP for Coding Agents description: ZCP gives coding agents current project knowledge and bounded operations on one Zerops project — discover, deploy, verify, recover, and hand off. --- -Coding agents need current project knowledge and a safe way to act on it. The hard part is rarely "write the function" — it's letting the agent connect product intent to infrastructure: services, env vars, logs, deploys, verification, and a path to production. +Coding agents need current project knowledge and a safe way to act on it. The hard part is rarely "write the function" - it's letting the agent connect product intent to infrastructure: services, env vars, logs, deploys, verification, and a path to production. **Zerops Control Plane (ZCP)** gives a coding agent current project state and bounded operations on one Zerops project. You describe the app or change you want; ZCP lets the agent discover services, use existing infrastructure or create what is missing, wire the app to managed services, deploy through the standard pipeline, verify the real endpoint, inspect logs, and report a URL or blocker. You hold the product intent, constraints, approvals, and quality bar. +The useful split is simple: **ZCP holds project reality; you hold intent.** The agent should not need you to paste a service map, env wiring plan, deploy log, or recovery checklist before it can do useful app work. + :::caution Keep production in a separate project ZCP runs with a project-scoped token that grants operational rights inside that project. Put it in a development or staging project, not the project serving production traffic. That keeps agent-driven changes away from production unless your CI or release process promotes them. See [Production boundary](/zcp/security/production-policy). ::: @@ -15,7 +17,7 @@ ZCP runs with a project-scoped token that grants operational rights inside that Developers using a coding agent on application work that needs real infrastructure. The developer stays focused on the product outcome, technology choices, acceptance criteria, and judgment. ZCP gives the agent the platform context and operations needed to use or create services, connect the app to infrastructure, deploy, verify, recover from failure, and report back. -The **Include Coding Agent** option currently bundles Claude Code and wires it to ZCP. The agent account remains yours: authenticate it with your own subscription login or API credentials. Additional bundled agents are planned. Local setup can connect a compatible local agent client after `zcp init`. +The **Include Coding Agent** option currently bundles Claude Code and wires it to ZCP. The agent account remains yours: authenticate it with your own subscription login or API credentials. Additional bundled agents are planned. Local setup can connect a compatible agent client running on your machine. ## What ZCP lets the agent do @@ -38,21 +40,23 @@ Full workflow terms: [Workflow terms](/zcp/reference/agent-workflow). The `zcp` binary can run in a Zerops service or on your laptop. The user choice is remote setup or local setup. -**Remote setup** runs ZCP inside a Zerops `zcp@1` service. Enable **Include Coding Agent** for the bundled agent CLI, and **Cloud IDE** for Browser VS Code. This is the recommended starting point when broad agent permissions can stay inside the project. [Use remote workspace](/zcp/setup/hosted-workspace) +**Remote setup** runs ZCP inside a Zerops `zcp@1` service. Enable **Include Coding Agent** for the bundled agent CLI, and **Cloud IDE** for Browser VS Code. This is the recommended starting point when broad agent permissions can stay inside the project. [What remote workspace gives you](/zcp/setup/hosted-workspace) -**Local setup** runs the `zcp` binary on your laptop after `zcp init`. The agent runs in your local editor or CLI; private service access uses `zcli vpn up`. Use it when local files, tools, data, or a local-only agent client should stay in charge. [Set up local ZCP](/zcp/setup/local-agent-bridge) +**Local setup** runs ZCP on your laptop. The agent runs in your local editor or CLI; private service access uses Zerops VPN. Use it when local files, tools, data, or a local-only agent client should stay in charge. [How to run ZCP on your machine](/zcp/setup/local-agent-bridge) Either way, the agent gets the same project-scoped ZCP operations against the same project. Remote setup can package ZCP with an agent CLI and browser editor; local setup connects your own agent client to ZCP. +ZCP is also MCP-native. Advanced integrations can call the same project operations directly without using the Claude Code workflow layer; in that case your client owns the policy and sequencing. See [Advanced operations](/zcp/reference/mcp-operations#zcp-as-direct-mcp-tools). + Decision: [Remote or local ZCP setup](/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 remote setup adds Remote setup starts with a `zcp@1` Zerops service. **Include Coding Agent** adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use ZCP. **Cloud IDE** adds Browser VS Code so you can supervise or take over from the dashboard. -Without **Include Coding Agent**, there is no preconfigured agent waiting inside the service. Without **Cloud IDE**, there is no browser editor. Configure both when you provision the service if you want the first-run agent + IDE path. Details: [Use remote workspace](/zcp/setup/hosted-workspace#what-you-get). +Without **Include Coding Agent**, there is no preconfigured agent waiting inside the service. Without **Cloud IDE**, there is no browser editor. Configure both when you provision the service if you want the first-run agent + IDE path. Details: [What remote workspace gives you](/zcp/setup/hosted-workspace#what-it-includes). -Remote setup is still your workspace. You can install another agent CLI, add private MCP servers or helper processes, edit `CLAUDE.md` or other agent instruction files, and keep team tooling in the service. ZCP supplies the project-scoped control plane, operations, and workflow evidence; it does not lock the service to one agent or one set of tools. +Remote setup is still your workspace. You can install another agent CLI, add private MCP servers or helper processes, keep team tooling in the service, or use ZCP from another MCP-capable client. ZCP supplies the project-scoped control plane, operations, and workflow evidence; it does not lock the service to one agent or one set of tools. ## Why transparent infrastructure works for agents diff --git a/apps/docs/content/features/local-remote-development.mdx b/apps/docs/content/features/local-remote-development.mdx index 71627f3c4..7041b601d 100644 --- a/apps/docs/content/features/local-remote-development.mdx +++ b/apps/docs/content/features/local-remote-development.mdx @@ -7,6 +7,8 @@ You can develop on Zerops without leaving your laptop, fully inside Zerops, or a This is also what narrows the gap between development and production. Development and staging can use the same Zerops-managed service types, networking model, and `zerops.yaml` patterns production uses. Production should still live in a separate project with separate credentials, access rules, and resource policies. +The same substrate works for humans and coding agents. A local agent inherits your laptop's VPN reach; an agent inside remote setup inherits the project's private network; an editor over SSH inherits the service or workspace it connects to. ZCP adds project operations and workflow evidence on top of that access, but local and remote development do not require ZCP. + :::note Remote setup is a `zcp@1` service you can add to a development or staging project. It can provide a Linux dev container, optional Cloud IDE, and optional **Include Coding Agent** wiring. The paths below can use that service, connect directly to runtime services, or stay fully local over VPN; local development with `zcli vpn up` does not require ZCP at all. ::: @@ -39,7 +41,7 @@ What you get on your laptop: every service in the project addressable by hostnam What you don't have to do: add remote setup, 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 service type as stage or production. -This path is also where coding agents running on your laptop fit. Without ZCP, a local agent client inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. To let the agent operate the project - discover, deploy, verify, read logs, and manage delivery handoff - set up [local ZCP](/zcp/setup/local-agent-bridge). +This path is also where coding agents running on your laptop fit. Without ZCP, a local agent client inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. To let the agent operate the project - discover, deploy, verify, read logs, and manage delivery handoff - add [ZCP on your machine](/zcp/setup/local-agent-bridge). **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. @@ -51,7 +53,7 @@ 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 remote setup (a `zcp@1` service) to a development or staging project - see [Use remote workspace](/zcp/setup/hosted-workspace) - you get a project-contained workspace with `zcp`, private-network access, optional Browser VS Code, and optional bundled agent wiring. +When you add remote setup (a `zcp@1` service) to a development or staging project - see [What remote workspace gives you](/zcp/setup/hosted-workspace) - you get a project-contained workspace with `zcp`, private-network access, optional Browser VS Code, and optional bundled agent wiring. Remote setup can also mount runtime service files into the workspace, so you can inspect or edit code that runs in another container without moving it to your laptop. Details live in [Runtime file access](/zcp/setup/hosted-workspace#runtime-file-access). @@ -115,7 +117,7 @@ The development environments above don't approximate production by inventing a d 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 and stage, you've already tested it against the kind of infrastructure it'll meet in production. The remaining differences are scale, credentials, access policy, and data. -This applies whether the developer is human or an agent — see [ZCP for Coding Agents](/features/coding-agents) for the agent case. +This applies whether the developer is human or an agent. ZCP is the agent-facing control plane on top of the same project substrate; see [ZCP for Coding Agents](/features/coding-agents) for that case. ## How this differs from cloud IDEs @@ -133,6 +135,6 @@ Remote setup is just a Zerops service. It's a container running the `zcp@1` Ubun - 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 → [ZCP for Coding Agents](/features/coding-agents) -- Use remote workspace → [Use remote workspace](/zcp/setup/hosted-workspace) +- What remote workspace gives you → [What remote workspace gives you](/zcp/setup/hosted-workspace) - Remote or local ZCP setup → [Remote or local ZCP setup](/zcp/setup/choose-workspace) - Term reference → [ZCP Glossary](/zcp/glossary) diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx index 4e9a690ad..53bd514de 100644 --- a/apps/docs/content/zcp/concept/how-it-works.mdx +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -1,19 +1,19 @@ --- title: 'How ZCP works' -description: 'The execution model behind ZCP: project state, runtime scope, service setup, app work, verification, recovery, and delivery.' +description: 'The ZCP operating loop: live state, runtime target, service setup, app work, verification, recovery, and delivery.' --- -ZCP gives a coding agent an operating loop for one Zerops project. The agent starts from live project state, chooses the app runtime and dependencies, makes the application change, deploys through Zerops, verifies real behavior, and then returns proof or a concrete blocker. +ZCP gives a coding agent an operating loop for Zerops app work. The agent reads live state, chooses the app runtime and dependencies, changes the app, deploys through Zerops, verifies real behavior, and returns proof or a blocker. -The loop is carried by three things: project-scoped tools, Zerops-specific knowledge, and workflow instructions that tell the agent what to inspect, what it can change, when it should ask, and what counts as done. The point is not that you run a workflow by hand. The point is that the agent has a project-aware path behind a product request and can make the important decisions visible while it works. +The loop is carried by three things: bounded Zerops operations, Zerops-specific knowledge, and instructions for what to inspect, what to change, when to ask, and what counts as done. You do not run the loop by hand. You describe the product outcome; the agent uses the loop to make its decisions visible while it works. ## The control loop ```mermaid flowchart TD intent["Product intent
Build a task board for my team."] - state["Live project state
services, runtime layout, env vars,
logs, events, saved work state"] - scope["Runtime scope
which app service changes"] + state["Live state
services, runtime layout, env vars,
logs, events, saved work state"] + scope["Runtime target
which app service changes"] setup{"Missing or unsuitable
services?"} provision["Service setup
use existing services or create
missing runtimes/dependencies"] appwork["App work
code, zerops.yaml, env refs,
migrations, seeds, framework config"] @@ -23,7 +23,7 @@ flowchart TD evidence["Evidence
build logs, runtime logs,
events, verify output"] proof["Proof
URL, endpoint result,
UI state, or stored result"] delivery["Delivery after proof
keep direct deploy, push to git,
or hand off"] - blocker["Blocker
credential, decision,
unsupported fit, repeated failure"] + blocker["Blocker
credential, decision,
unsupported fit,
repeated failure"] intent --> state --> scope --> setup setup -->|yes| provision --> appwork @@ -50,11 +50,11 @@ flowchart TD class blocker stop; ``` -The loop gives the agent a way to resolve each phase from project evidence instead of from a guessed checklist. It knows where to read current state, which operations are scoped to the project, which Zerops rules apply to app wiring, which evidence to read after deploy, and when the result is proof versus a blocker. +The loop keeps the agent working from evidence instead of a guessed checklist. It knows where to read current state, which Zerops rules apply to app wiring, which evidence to read after deploy, and when the result is proof versus a blocker. -## What "project state" means +## What "live state" means -ZCP reads the project instead of asking you to paste a service inventory into the prompt. Useful state includes: +ZCP reads Zerops instead of asking you to paste a service inventory into the prompt. Useful state includes: - runtime services and managed services, - whether the app has one runtime, a dev+stage pair, or local files linked to a Zerops runtime, @@ -63,7 +63,7 @@ ZCP reads the project instead of asking you to paste a service inventory into th - build logs, runtime logs, and verification output, - saved work state after an interrupted session. -This is why a short prompt can be enough. The agent can ask ZCP what exists, which runtime was last deployed, which checks passed, and where a previous run stopped. +This is why a short prompt can be enough. The agent can ask what exists, which runtime was last deployed, which checks passed, and where a previous run stopped. Chat history is not the source of truth. If the agent sounds confused, starts from an old assumption, or a session was interrupted, the recovery move is: @@ -71,20 +71,20 @@ Chat history is not the source of truth. If the agent sounds confused, starts fr Read ZCP status and tell me where this project stands before changing anything. ``` -## What ZCP coordinates behind the prompt +## What ZCP coordinates for the agent -ZCP is intentionally opinionated about the concerns an agent has to resolve during app work. You usually do not name these concerns in the prompt. ZCP provides them through project state, tools, guidance, and workflow instructions so the agent can work from evidence. +ZCP is opinionated about the concerns an agent must resolve during app work. You usually do not name them in the prompt. ZCP supplies the state, tools, guidance, and done criteria so the agent can work from evidence. | Concern | What ZCP gives the agent | What that means for you | | ---------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | -| Project state | Live services, env-var keys, Zerops references, recent events, logs, verification output, and saved work state. | You do not have to paste a service inventory or reconstruct what happened. | -| Runtime target | A model for choosing the app runtime that should receive code changes. | The agent can identify where app work belongs before editing or deploying. | +| Live state | Services, env-var keys, Zerops references, recent events, logs, verification output, and saved work state. | You do not have to paste a service inventory or reconstruct what happened. | +| Runtime target | Rules for choosing the app runtime that should receive code changes. | The agent can identify where app work belongs before editing or deploying. | | Managed services | Knowledge and env wiring patterns for database, cache, queue, search, storage, mail, and similar services. | Product intent can imply real dependencies without a manual wiring checklist. | -| Service setup | Project-scoped operations for using existing services or creating missing runtimes and dependencies. | Runtime layout and dependencies can be established before app work starts. | +| Service setup | Operations for using existing services or creating missing runtimes and dependencies. | Runtime layout and dependencies can be established before app work starts. | | App wiring | Zerops-specific rules for `zerops.yaml`, env references, ports, commands, public access, and build/deploy behavior. | The app is wired for Zerops rather than for a generic cloud template. | | Deploy evidence | Build logs, runtime logs, platform events, service status, HTTP checks, and structured verification output. | A failed deploy becomes a diagnosis surface instead of guesswork. | | Behavior proof | A done gate based on the requested behavior, not only on a successful build or reachable root URL. | The final answer can point to what was actually checked. | -| Delivery handoff | A workflow contract for direct proof first, then git push, CI, or human handoff when that is the chosen path. | Shipping setup follows a verified running result. | +| Delivery handoff | Direct proof first; then git push, CI, or human handoff when that is the chosen delivery choice. | Shipping setup follows a verified running result. | The exact labels for layouts, delivery modes, and setup routes live in the [Glossary](/zcp/glossary) and [Workflow terms](/zcp/reference/agent-workflow). @@ -96,13 +96,13 @@ Before app code work starts, ZCP helps the agent make three decisions visible: - Which managed services are dependencies? - Does the existing project layout fit the request? -If the services already exist, the agent can use them. If a needed runtime or dependency is missing, ZCP can create it through project-scoped operations. If the choice affects cost, product scope, credentials, a destructive action, or which stage/runtime should be used, the workflow tells the agent to stop and ask. +If the services already exist, the agent can use them. If a needed runtime or dependency is missing, ZCP can create it. If the choice affects cost, product scope, credentials, a destructive action, or which stage/runtime should be used, the agent should stop and ask. -Service setup is finished when the app runtime and dependencies are known. It is not the finished app; it is the project layout that lets app work happen in the right place. +Service setup is finished when the app runtime and dependencies are known. It is not the finished app; it is the layout that lets app work happen in the right place. ## App work changes code and platform wiring together -After the runtime scope is known, ZCP gives the agent the platform knowledge needed to make the application change. In practice this often spans both source code and Zerops wiring: +After the runtime target is known, ZCP gives the agent the platform knowledge needed to make the application change. In practice this often spans both source code and Zerops wiring: - app files, - `zerops.yaml` build and run setup, @@ -113,7 +113,7 @@ After the runtime scope is known, ZCP gives the agent the platform knowledge nee ZCP guidance matters because Zerops is its own platform. Service references, build/deploy behavior, public access, scaling, and env resolution do not follow Docker Compose or Kubernetes conventions. The agent gets Zerops-specific rules instead of relying on a generic cloud template. -The first functional deploy goes through ZCP's direct deploy path. That deploy creates the running result the agent can verify. A repository push or CI handoff can follow, but it should not replace the first proof. +The first functional deploy goes directly through ZCP. That deploy creates the running result the agent can verify. A repository push or CI handoff can follow, but it should not replace the first proof. ## Recovery is evidence-driven @@ -124,11 +124,11 @@ When something fails, ZCP gives the agent the evidence surface that matches the | Build failed | Build logs, build commands, dependency manifests, deploy file list. | | Runtime failed to start | Runtime or prepare logs, start command, ports, env references. | | Route or behavior failed | Verify output, HTTP response, runtime logs at request time, stored state. | -| Network path failed | VPN, SSH, DNS, subdomain readiness, service status, named transport error. | +| Network access failed | VPN, SSH, DNS, subdomain readiness, service status, named transport error. | | Config was rejected | Field-level rejection, `zerops.yaml`, setup name, env reference, service settings. | | Credential failed | The named credential surface: Zerops token, git token, SSH, managed-service credential, or external API key. | -Retrying the same deploy without new evidence is not progress. The workflow pushes the agent toward one of three outcomes: fix from a cause, ask for the missing decision, or report a blocker with the category, evidence read, and attempts made. +Retrying the same deploy without new evidence is not progress. The loop pushes the agent toward one of three outcomes: fix from a cause, ask for the missing decision, or report a blocker with the category, evidence read, and attempts made. ## Verification has two layers @@ -155,30 +155,30 @@ Delivery choice controls how future changes ship after a verified runtime exists | Push to git | You want commits, review, repository history, or a repo-triggered build. | | External handoff | CI, release management, or a human owns the next deploy. | -Packaging a running service is a separate advanced reuse path. It turns a deployed runtime into a re-importable bundle for another Zerops project. It is useful for handoff or reuse, not for deploying the next app change; see [Package a running service](/zcp/workflows/package-running-service). +Packaging a running service is a separate advanced reuse task. It turns a deployed runtime into a re-importable bundle for another Zerops project. It is useful for handoff or reuse, not for deploying the next app change; see [Package a running service](/zcp/workflows/package-running-service). ## Remote and local setup use the same loop -The same `zcp` binary can run in two places. The control loop stays the same; the filesystem and network path change. +The same `zcp` binary can run in two places. The control loop stays the same; the filesystem and network access change. -| Path | What runs where | Practical effect | +| Setup | What runs where | Practical effect | | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Remote setup | A `zcp@1` service runs the `zcp` binary inside the Zerops project. **Include Coding Agent** adds the bundled agent CLI; **Cloud IDE** adds browser VS Code. | Work happens inside the project boundary. The agent can use project-private networking and runtime file mounts. | -| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your local editor or CLI agent talks to it. | App files, local data, deploy source, and git credentials stay on your machine. Managed services are reached over Zerops VPN, and `.env` generation bridges project credentials into your local app. | +| Remote setup | A `zcp@1` service runs the `zcp` binary inside Zerops. **Include Coding Agent** adds the bundled agent CLI; **Cloud IDE** adds Browser VS Code. | Work happens inside the remote workspace. The agent can use private networking and runtime file mounts. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your local editor or CLI agent talks to it. | App files, local data, deploy source, and git credentials stay on your machine. Managed services are reached over Zerops VPN, and `.env` generation bridges credentials into your local app. | -Base the setup path on where you want the agent and filesystem to live: [Remote or local ZCP setup](/zcp/setup/choose-workspace). +Choose setup by where the agent and filesystem should live: [Remote or local ZCP setup](/zcp/setup/choose-workspace). ## Signs of a healthy ZCP run A well-shaped run should: -- name the runtime scope before editing or deploying, +- name the runtime target before editing or deploying, - use existing services when they fit, - create missing services only before app work starts, - read logs, events, and verify output when failure occurs, - distinguish runtime reachability from requested behavior, - stop before destructive actions, ambiguous runtime/stage choices, or missing credentials, -- end with proof or a concrete blocker. +- end with proof or a blocker. That is the practical difference between "the agent wrote code" and "the app task is done". diff --git a/apps/docs/content/zcp/glossary.mdx b/apps/docs/content/zcp/glossary.mdx index 19f8f317e..d5fe94770 100644 --- a/apps/docs/content/zcp/glossary.mdx +++ b/apps/docs/content/zcp/glossary.mdx @@ -7,9 +7,9 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, ## Core names -**ZCP** - Zerops Control Plane for coding agents: the project-scoped control plane documented in this section. +**ZCP** - Zerops Control Plane for coding agents: the Zerops operations and workflow layer documented in this section. -**ZCP operations** - the project-scoped operations surface exposed to a coding agent. In MCP clients, this usually appears as the `zerops` server. +**ZCP operations** - the bounded Zerops operations exposed to a coding agent. In MCP clients, this usually appears as the `zerops` server. **MCP server** - the Model Context Protocol server that exposes ZCP operations to an agent client. @@ -27,21 +27,21 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, ## Where ZCP runs -**Remote setup** - ZCP running inside a Zerops `zcp@1` service in the project. +**Remote setup** - ZCP running inside a Zerops `zcp@1` service. -**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use the project's ZCP operations. +**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use ZCP operations. **Cloud IDE** - browser-based VS Code served by remote setup. **Browser VS Code** - the dashboard entry point into the Cloud IDE. -**AI Agent environment** - a recipe environment preset that creates app services plus remote setup with **Include Coding Agent** enabled. It is a recipe preset, not a separate ZCP setup path. +**AI Agent environment** - a recipe environment preset that creates app services plus remote setup with **Include Coding Agent** enabled. It is a recipe preset, not a separate ZCP setup choice. **Local setup** - the `zcp` binary running on your laptop after `zcp init`, while your editor or CLI agent talks to it. **Local agent bridge** - local setup with the `zcp` binary connected to a local agent client. -**Env bridge** - local setup behavior that writes a local `.env` snapshot from project env vars and Zerops references so local app code can reach managed services over VPN. +**Env bridge** - local setup behavior that writes a local `.env` snapshot from Zerops env vars and references so local app code can reach managed services over VPN. ## Work phases and loops @@ -49,7 +49,7 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, **Infrastructure setup** - user-facing synonym for service setup. -**Setup route** - the exact label for how service setup starts: +**Setup route** - exact reference label for how service setup starts: - `adopt` - runtime services already exist and should be used. - `recipe` - an empty or ZCP-only project matches a known recipe/stack. @@ -60,7 +60,7 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, **Develop flow** - exact reference name for the deploy, verify, and fix loop. -**Runtime scope** - the app runtime the change targets, such as `appdev`, `appstage`, `app`, or a linked local target. +**Runtime target** - the app runtime the change targets, such as `appdev`, `appstage`, `app`, or a linked local target. **Direct deploy** - a ZCP deploy from the current source to the scoped runtime. The first verified running result uses direct deploy before delivery setup is applied. @@ -92,9 +92,9 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, **External handoff** - delivery mode where ZCP records proof and state, but a human or external system owns future deploys. -**Package a running service** - advanced reuse workflow that turns one deployed runtime and its managed dependencies into a re-importable bundle. It is not the normal path for deploying the next app change. +**Package a running service** - advanced reuse workflow that turns one deployed runtime and its managed dependencies into a re-importable bundle. It is not the normal way to deploy the next app change. -**Packaging env bucket** - exact labels used when classifying project env vars during packaging: +**Packaging env bucket** - exact labels used when classifying env vars during packaging: - `infrastructure` - value comes from a managed-service reference and should be regenerated by the new project. - `auto-secret` - local app secret that should be freshly generated on re-import. @@ -145,9 +145,9 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, ## Credentials -**Project-scoped token** - Zerops token that can access exactly one project. ZCP expects this scope. +**Single-project token** - Zerops token that can access exactly one project. ZCP expects this shape. -**`ZCP_API_KEY`** - project-scoped Zerops token used by ZCP. +**`ZCP_API_KEY`** - single-project Zerops token used by ZCP. **`GIT_TOKEN`** - git provider credential used by remote git-push delivery when needed. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index 908c7cfa3..0a77b5538 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -1,21 +1,21 @@ --- title: 'Zerops Control Plane for coding agents' -description: 'Why ZCP changes how a coding agent works with a Zerops project, and what you can leave out of the prompt.' +description: 'Why ZCP changes how a coding agent works with Zerops, and what you can leave out of the prompt.' --- import Image from '/src/components/Image'; -ZCP is a project-scoped control plane for AI coding agents over one Zerops project. It gives the agent the current truth about that project, project-scoped tools for operating it, and rules for deciding what should happen next and when the work is done. +ZCP is the project-scoped control plane for coding-agent work on Zerops. Zerops gives developers production-shaped infrastructure from the start: runtimes, managed services, private networking, env references, the deploy pipeline, logs, and public routes. ZCP gives the agent that same operating surface in a development or staging project, so its output can be verified on real infrastructure before production promotion. -The practical effect is that the agent can work from the project's real state instead of a guessed checklist. It can identify the right app runtime and managed services, make Zerops changes through project-scoped operations, read evidence when something fails, and finish with proof or a concrete blocker. +A ZCP-backed task should end in one of two states: proof or a blocker. Proof means a deployed runtime plus the URL, endpoint response, UI state, worker result, or stored data that shows the requested behavior works. A blocker means the agent read the relevant Zerops evidence and names the missing credential, decision, unsupported fit, or repeated failure. -That changes the job you give the agent. You provide the product intent, technical constraints, and quality bar. ZCP provides the platform reality: what exists, where code runs, which dependencies are available, what was deployed, what passed verification, and what the next evidence-backed step should be. +Your prompt can stay about the product. Name the stack, runtime layout, acceptance criteria, delivery path, external credentials, or risky approval only when those decisions matter. ## What ZCP gives the agent -**Project truth.** Services, app runtime layout, managed dependencies, env-var keys and references, logs, events, deploy history, verification state, and current work state. +**Current state.** Services, app runtime layout, managed dependencies, env-var keys and references, logs, events, deploy history, verification state, and current work state. -**Zerops control.** Project-scoped operations for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. +**Zerops operations.** Tools for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. **Workflow.** ZCP combines two work loops: infrastructure for using existing Zerops services or creating missing ones, and development for code, `zerops.yaml`, deploy, verification, and delivery choice. The goal is to keep Zerops work aligned with platform and development best practices while staying behind the product task, not becoming another checklist. @@ -28,16 +28,16 @@ Without ZCP, an app prompt often turns into an operations runbook. With ZCP, you - the service map, runtime target, dev/stage state, or managed-service inventory, - database credentials, private hostnames, env-var references, or generated connection strings, - build logs, runtime logs, event timelines, or a guess about why the last deploy failed, -- a deploy/verify/recovery script for every task; ZCP treats proof or a concrete blocker as part of completion, +- a deploy/verify/recovery script for every task; ZCP treats proof or a blocker as part of completion, - a recap after the chat loses context; the agent can read current ZCP status. **You still own the decisions that need human judgment:** product intent, technology constraints, acceptance criteria, external credentials, repository policy, and approval for destructive actions. ## Where ZCP runs -The **same `zcp` binary** runs in both setups. In [remote setup](/zcp/setup/hosted-workspace), Zerops packages it inside a `zcp@1` service in the project. In [local setup](/zcp/setup/local-agent-bridge), you install the binary on your machine and connect your own editor or CLI agent to the project. The mental model is the same: one project, live state, scoped operations, deploy/verify evidence. The filesystem, network path, deploy source, and safety profile differ. +The **same `zcp` binary** runs in both setups. In [remote setup](/zcp/setup/hosted-workspace), Zerops packages it as a `zcp@1` service with Browser VS Code and bundled agent wiring. In [local setup](/zcp/setup/local-agent-bridge), you install the binary on your machine and connect your own editor or CLI agent. The model is the same: live state, bounded operations, deploy/verify evidence. The workspace, network access, deploy source, and safety profile differ. -To start using ZCP, choose one path: add remote setup to a Zerops project, or initialize local setup beside your editor or CLI agent. The [Quickstart](/zcp/quickstart) uses remote setup because it needs no local install. +To start using ZCP, choose a setup: add remote setup in Zerops, or initialize local setup beside your editor or CLI agent. The [Quickstart](/zcp/quickstart) uses remote setup because it needs no local install.

Remote setup with the zcp service in Zerops and Claude Code running in browser VS Code
## Your agent, credentials, and workspace -**Agent account.** Remote setup can include a bundled agent CLI, currently Claude Code, already configured to use ZCP. Zerops wires the agent to the project; it does not provide or own your model account. You authenticate the agent with your own subscription login or API credentials. +**Agent account.** Remote setup can include a bundled agent CLI, currently Claude Code, already configured to use ZCP. Zerops wires the agent to the control plane; it does not provide or own your model account. You authenticate the agent with your own subscription login or API credentials. -**Zerops token.** ZCP itself connects to Zerops through a project-scoped Zerops token. In remote setup, Zerops injects that token into the `zcp@1` service. In local setup, you provide a project-scoped token when you initialize ZCP beside your editor or CLI agent. Token details live in [Tokens and credentials](/zcp/security/tokens-and-project-access). +**Zerops token.** ZCP connects through `ZCP_API_KEY`, a Zerops token limited to one project. Remote setup gets it from the platform; local setup reads it from your `.mcp.json`. Token details live in [Tokens and credentials](/zcp/security/tokens-and-project-access). -**Workspace freedom.** The `zcp@1` service is still a normal project service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and adapt the workspace around the bundled setup. ZCP is the project control plane available to the agent; it is not a closed agent product. Details live in [Use remote workspace](/zcp/setup/hosted-workspace#make-customization-persistent). +**Workspace freedom.** The `zcp@1` service is still a normal Zerops service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and adapt the workspace around the bundled setup. ZCP is not a closed agent product. Details live in [What remote workspace gives you](/zcp/setup/hosted-workspace#make-customization-persistent). :::caution Production boundary -Use ZCP in development or staging projects. Production should stay in a separate Zerops project and receive promoted work through your CI or release process; see [Production boundary](/zcp/security/production-policy). +Use ZCP for development or staging work. Production should stay in a separate Zerops project and receive promoted work through your CI or release process; see [Production boundary](/zcp/security/production-policy). ::: ## Start here -First-read path: +First-read pages: | If you want to | Read | | ----------------------------------------- | -------------------------------------------------------- | | Try the guided hands-on path | [Quickstart](/zcp/quickstart) | -| Understand what happens behind the prompt | [How ZCP works](/zcp/concept/how-it-works) | +| Understand the agent operating loop | [How ZCP works](/zcp/concept/how-it-works) | | Compare remote and local setup | [Remote or local ZCP setup](/zcp/setup/choose-workspace) | | Start building after setup | [Build with ZCP](/zcp/workflows/build-with-zcp) | @@ -76,10 +76,10 @@ Specific tasks and reference: | If you want to | Read | | ---------------------------------------- | --------------------------------------------------------------------------- | -| Use remote workspace in a project | [Use remote workspace](/zcp/setup/hosted-workspace) | -| Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | +| Understand remote workspace | [What remote workspace gives you](/zcp/setup/hosted-workspace) | +| Use your local editor or CLI agent | [How to run ZCP on your machine](/zcp/setup/local-agent-bridge) | | Decide how finished work ships | [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | -| Understand tokens and project boundaries | [Trust model](/zcp/security/trust-model) | +| Understand token access and boundaries | [Trust model](/zcp/security/trust-model) | | Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | | Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | | Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | diff --git a/apps/docs/content/zcp/quickstart.mdx b/apps/docs/content/zcp/quickstart.mdx index 862476d89..4ddbbe431 100644 --- a/apps/docs/content/zcp/quickstart.mdx +++ b/apps/docs/content/zcp/quickstart.mdx @@ -1,20 +1,20 @@ --- title: 'Quickstart' -description: 'Try ZCP through a ready-made AI Agent recipe: deploy Laravel showcase, authorize Claude Code, open Browser VS Code, and ask for a product change.' +description: 'Try ZCP with a ready-made AI Agent recipe, Browser VS Code, Claude Code, and one product change.' --- import Image from '/src/components/Image'; -Use this path when you want to see ZCP working with the least setup. It is intentionally one path: deploy a recipe that already includes the **AI Agent** environment, authorize Claude Code, open Browser VS Code, and ask for product work in natural language. +Use this when you want the fastest hands-on ZCP loop: deploy an **AI Agent** recipe, authorize Claude Code, open Browser VS Code, and ask for one product change. -Zerops has a [recipes catalog](https://app.zerops.io/recipes) for many runtimes and frameworks. Some recipes include an **AI Agent** environment, which is the ready-to-go ZCP setup: app services, managed services, the `zcp@1` workspace service, Browser VS Code, and bundled agent wiring in one deploy. +Some [Zerops recipes](https://app.zerops.io/recipes) include an **AI Agent** environment. That preset creates app services, managed services, the `zcp@1` workspace, Browser VS Code, and bundled agent wiring in one deploy. -This quickstart uses the [Laravel showcase recipe](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent) because it includes real managed services and the AI Agent environment. The same flow works for other recipes that offer AI Agent. +This quickstart uses [Laravel showcase](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent) because it includes real managed services. The same flow works for other recipes that offer AI Agent (local variants exist too). ## Prerequisites - A Zerops account with permission to create a project. -- A Claude Code subscription login or API authentication. Claude Code is the bundled agent in the current dashboard flow; support for additional bundled agents is planned. Zerops wires Claude Code to ZCP, but your agent subscription or model credentials stay yours. +- A Claude Code subscription login or API authentication. Zerops wires Claude Code to ZCP, but your agent subscription or model credentials stay yours. ## 1. Choose the AI Agent recipe @@ -34,15 +34,15 @@ This quickstart uses the [Laravel showcase recipe](https://app.zerops.io/recipes /> -The deploy creates the app runtimes, managed dependencies, and a workspace service using the `zcp@1` service type. In this recipe it appears as the `zcp` service. That service is where the agent, terminal, and browser IDE run. App code still deploys to the app runtime services, not to `zcp`. +The deploy creates app runtimes, managed dependencies, and a `zcp@1` workspace. In this recipe it appears as the `zcp` service. The agent, terminal, and browser IDE run there. App code still deploys to the app runtimes, not to `zcp`. ## 2. Authorize Claude Code When provisioning finishes, the dashboard opens the Claude Code authentication flow. Use your own Claude Code subscription login or API credentials. -This is separate from the ZCP project token. Zerops injects the project-scoped `ZCP_API_KEY` into the workspace service; your Claude account or API key is used only by the bundled agent. +This is separate from `ZCP_API_KEY`, the Zerops token used by ZCP. Your Claude login or API key is used only by the bundled agent. -If you close the prompt, open the project in the dashboard and then open the `zcp` service. The control-plane panel shows the browser workspace, web terminal, SSH path, desktop IDE path, and agent authorization state. +If you close the prompt, open the `zcp` service in the dashboard. Its panel shows the browser workspace, web terminal, SSH access, desktop editor access, and agent authorization state.
Claude Code authorization dialog shown over the ZCP service control plane and Browser VS Code terminal
## 3. Open the workspace -After authentication, continue into **Browser VS Code**. The workspace opens with the project filesystem, ZCP configuration, terminal access, and the Claude Code panel available in the editor. +After authentication, continue into **Browser VS Code**. The workspace opens with files, ZCP configuration, terminal access, and the Claude Code panel.
Browser VS Code workspace with the Claude Code panel open
-You are now inside the remote workspace. The agent can read project state, use project-scoped ZCP operations, reach project-private services, and deploy app changes to the runtime services created by the recipe. +You are now inside the remote workspace. The agent can read live state, use ZCP operations, reach private services, and deploy app changes to the runtimes created by the recipe. ## 4. Ask for a product outcome @@ -79,9 +79,9 @@ Build a task board for my team. Tasks should stay saved after refresh. ``` -The agent is expected to deploy, verify, read evidence, and return proof for the app task. Reserve extra prompt detail for decisions that change the work. +The agent should deploy, verify, read evidence, and return proof for the app task. Add detail only when it changes the work. -Add details when they change the product, stack, runtime layout, acceptance criteria, or delivery path: +Add details when they change the product, stack, runtime layout, acceptance criteria, or delivery preference: ```text In this Laravel app, add a task board backed by the existing PostgreSQL service. @@ -90,23 +90,23 @@ A user can create a task, refresh the page, and still see it. :::note Prompt shape -Short prompts work when they describe the product outcome. Add stack, runtime layout, acceptance criteria, delivery preference, external credentials, or destructive-action approval only when those decisions matter. ZCP carries project discovery, Zerops wiring, deploy, verification, evidence reading, and recovery behind that request. +Quickstart prompts are short so you can see what ZCP carries behind the request. Longer, detailed prompts are fine for larger engineering work, especially when they change product behavior, stack, runtime layout, acceptance criteria, delivery preference, credentials, or safety approvals. ::: ## 5. Read the proof -The final answer should give you a real URL, endpoint, UI state, stored result, or a concrete blocker. Open the URL and try the core behavior the agent says it verified. +The final answer should give you a real URL, endpoint, UI state, stored result, or blocker. Open the URL and try the behavior the agent says it verified. -For the task-board prompt, create a task, refresh the page, and confirm the task is still there. If it disappears, the app is not done; ask the agent to verify that exact behavior again and continue from the project evidence rather than starting over. +For the task-board prompt, create a task, refresh the page, and confirm the task is still there. If it disappears, the app is not done; ask the agent to verify that exact behavior again and continue from current evidence rather than starting over. -If the agent cannot finish, the useful output is a concrete blocker: the missing credential, decision, unsupported runtime choice, or repeated failure it could not recover from. +If the agent cannot finish, useful output names the blocker: missing credential, missing decision, unsupported runtime choice, or repeated failure it could not recover from. ## Next steps -Read [How ZCP works](/zcp/concept/how-it-works) if you want the model behind what just happened: project state, runtime fit, app wiring, deploy evidence, behavior proof, and blockers. +Read [How ZCP works](/zcp/concept/how-it-works) for the model behind what just happened: live state, runtime fit, app wiring, deploy evidence, behavior proof, and blockers. -Read [Remote or local ZCP setup](/zcp/setup/choose-workspace) when you are deciding where the agent workspace should live for a real project. Remote setup keeps the agent inside Zerops; local setup keeps the agent next to your local files, editor, data, and tools. +Read [Remote or local ZCP setup](/zcp/setup/choose-workspace) when you are deciding where the agent workspace should live. Remote setup keeps the agent inside Zerops; local setup keeps it next to your files, editor, data, and tools. Read [Build with ZCP](/zcp/workflows/build-with-zcp) before giving the agent larger tasks. It explains the practical choices you can control during normal work: runtime layout, development context, and delivery preference. -Before promoting anything to production, read [Production boundary](/zcp/security/production-policy). ZCP belongs in development or staging projects; production should stay in a separate Zerops project and receive verified work through your release path. +Before promoting anything to production, read [Production boundary](/zcp/security/production-policy). ZCP belongs in development or staging; production should receive verified work through your release process. diff --git a/apps/docs/content/zcp/reference/agent-workflow.mdx b/apps/docs/content/zcp/reference/agent-workflow.mdx index 68dec8296..78b62ec75 100644 --- a/apps/docs/content/zcp/reference/agent-workflow.mdx +++ b/apps/docs/content/zcp/reference/agent-workflow.mdx @@ -1,9 +1,9 @@ --- title: 'Workflow terms' -description: 'Precise ZCP workflow terms: session layers, service setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence.' +description: 'Precise ZCP terms for setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence.' --- -Exact vocabulary for ZCP workflows. Day-to-day prompts should describe outcomes; reference, audits, policies, and handoffs can use these names when precision matters. +Exact vocabulary for ZCP reference, audits, policies, and handoffs. Day-to-day prompts should describe outcomes. ## Session layers @@ -19,7 +19,7 @@ The `zcp` service is the control surface, not the app runtime. ## Service setup routes -Before app code work starts, ZCP reads project state and chooses a setup route. The route is an implementation detail in first-read docs, but it is useful in reference and audits. +Before app code work starts, ZCP reads live state and chooses a setup route. The route is an implementation detail in first-read docs, but it is useful in reference and audits. ```mermaid flowchart TD @@ -44,7 +44,7 @@ flowchart TD | --------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | | `adopt` | Runtime services already exist, including recipe-created projects. | Recreating services that exist, or targeting `zcp` as the app. | | `recipe` | The project is empty or only has ZCP, and the request matches a known stack recipe. | Deploying an unchanged starter as if it were the requested product. | -| `classic` | The project is empty and needs a custom service plan. | Writing app code before service ownership and runtime scope are known. | +| `classic` | The project is empty and needs a custom service plan. | Writing app code before service ownership and runtime target are known. | | `resume` | A previous setup was interrupted. | Starting from scratch without acknowledging live services and saved state. | Service setup stops when app runtimes and managed dependencies are known. App code, `zerops.yaml`, and the first deploy belong to the develop flow. @@ -55,7 +55,7 @@ The develop loop is the main ZCP work cycle. It closes only when reachability an ```mermaid flowchart TD - scope["1. Name runtime scope"] + scope["1. Name runtime target"] change["2. Change code/config"] deploy["3. Deploy in-scope runtime"] reach{"4. Runtime reachability
passes?"} @@ -72,9 +72,9 @@ flowchart TD fix --> blocker ``` -1. **Name runtime scope.** State which runtime is in scope (`appdev`, `appstage`, `app`, or linked local target). In dev+stage projects, dev work does not imply stage unless requested. +1. **Name runtime target.** State which runtime is in scope (`appdev`, `appstage`, `app`, or linked local target). In dev+stage projects, dev work does not imply stage unless requested. 2. **Change code/config.** Edit app files, `zerops.yaml`, env references, migrations, seeds, or framework config. -3. **Deploy directly first.** The first verified runtime deploy uses the direct ZCP deploy path. Delivery setup is applied after a verified running result exists. +3. **Deploy directly first.** The first verified runtime deploy goes through ZCP directly. Delivery setup is applied after a verified running result exists. 4. **Verify runtime reachability.** Service status, recent error logs, and HTTP probe for eligible runtime services. 5. **Verify requested behavior.** Endpoint body, UI state, job result, persisted data, or another check tied to the user request. 6. **Fix from evidence.** Read classification, logs, events, and check output. Repeating the same deploy without new evidence is not progress. @@ -93,7 +93,7 @@ Runtime layout describes which app runtime services ZCP should use. Zerops servi | `local-stage` | Local source directory linked to one Zerops runtime for deploys. | local directory + `appstage` | | `local-only` | Local source directory with no linked deploy target yet. | local directory | -The stage hostname is supplied by project state. ZCP should not invent it from the dev hostname. +The stage hostname comes from live state. ZCP should not invent it from the dev hostname. ## Delivery modes @@ -114,7 +114,7 @@ Git-push capability, delivery mode, and build integration are separate. A projec | `build` | Build phase failed. | Build logs and build commands. | | `start` | Build passed, runtime failed to start or crashed. | Prepare/runtime logs and start command. | | `verify` | Runtime exists but reachability or behavior failed. | Failing check detail and runtime logs at request time. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and network path. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and failed network access. | | `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Structured rejection detail and config files. | | `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | | `other` | No known category matched. | Raw event/log evidence; stop if repeated. | @@ -126,7 +126,7 @@ Categorization is what turns retries into evidence-driven fixes. Recovery moves A completed app task can answer: - which services were used or created, -- which runtime scope changed, +- which runtime target changed, - which deploy passed, - which reachability check passed, - which user-requested behavior passed, @@ -144,7 +144,7 @@ A green build with a broken route is not completion. A clear blocker is acceptab - Managed services are dependencies, not deploy targets. - Stage is explicit in `standard` projects; dev changes do not silently promote. - Runtime health and requested behavior are separate verification gates. -- Stop on repeated failure without new evidence, missing credentials, ambiguous runtime scope, or destructive recovery. +- Stop on repeated failure without new evidence, missing credentials, ambiguous runtime target, or destructive recovery. ## Auditing a session @@ -152,7 +152,7 @@ A session is well-shaped if platform evidence and the agent report answer: | Question | Evidence | | ----------------------------------------------- | ------------------------------------------------------------------------- | -| Which setup route and runtime layout were used? | ZCP status, service metadata, project services. | +| Which setup route and runtime layout were used? | ZCP status, service metadata, service list. | | Where did service setup end and app work begin? | Work-session state, deploy history, file changes. | | Which runtime was deployed and verified? | Service events, deploy result, verify output. | | What behavior proved success? | Endpoint/UI/job/data proof in the final report. | diff --git a/apps/docs/content/zcp/reference/index.mdx b/apps/docs/content/zcp/reference/index.mdx index 41afbc850..fa2e1da2c 100644 --- a/apps/docs/content/zcp/reference/index.mdx +++ b/apps/docs/content/zcp/reference/index.mdx @@ -1,14 +1,14 @@ --- title: "Reference" -description: "Precise ZCP terms, advanced operations, recovery shortcuts, and glossary." +description: "Precise ZCP terms, operation names, troubleshooting, packaging, and glossary." --- -Exact terms, operation names, and recovery detail live here. Day-to-day app work starts in [Build with ZCP](/zcp/workflows/build-with-zcp). +Use reference pages when exact labels matter. Day-to-day app work starts in [Build with ZCP](/zcp/workflows/build-with-zcp). | Need | Page | |---|---| -| Name service setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence | [Workflow terms](/zcp/reference/agent-workflow) | -| Look up common MCP operation names for agent-client policy or debugging | [Advanced operations](/zcp/reference/mcp-operations) | +| Name setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence | [Workflow terms](/zcp/reference/agent-workflow) | +| Use ZCP as direct MCP tools, understand generated `.zcp`/Claude files, or look up operation names | [Advanced operations](/zcp/reference/mcp-operations) | | Turn a running service into a re-importable bundle | [Package a running service](/zcp/workflows/package-running-service) | | Recover a stuck or confused session | [Troubleshooting](/zcp/reference/troubleshooting) | | Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | diff --git a/apps/docs/content/zcp/reference/mcp-operations.mdx b/apps/docs/content/zcp/reference/mcp-operations.mdx index 2c88a6474..d719b17a9 100644 --- a/apps/docs/content/zcp/reference/mcp-operations.mdx +++ b/apps/docs/content/zcp/reference/mcp-operations.mdx @@ -1,9 +1,57 @@ --- title: 'Advanced operations' -description: 'Common ZCP operation names for app-building, debugging, and automation.' +description: 'Use ZCP as direct MCP tools, understand generated state, and look up common operation names.' --- -Most users never need operation names. They matter when you configure agent-client policy, debug an MCP integration, or automate ZCP directly. In normal app work, ask the agent for outcomes. Recipe-authoring and ZCP-internal maintenance operations are intentionally out of scope. +Most users never need operation names. They matter when you configure agent-client policy, debug an MCP integration, or automate ZCP directly. For normal app work, ask the agent for outcomes. Recipe-authoring and ZCP-internal maintenance operations are intentionally out of scope. + +## ZCP as direct MCP tools + +ZCP is an MCP server. Workflow instructions are not required for every integration. + +Any MCP-capable client can connect to ZCP and call individual tools such as `zerops_discover`, `zerops_logs`, `zerops_events`, `zerops_deploy`, or `zerops_verify`. This is useful for custom agent clients, scripts, dashboards, policy-gated clients, or debugging sessions where you want one Zerops operation instead of a full ZCP-guided task. + +The higher-level ZCP behavior comes from generated agent instructions, especially `CLAUDE.md`. Those instructions tell Claude Code when to call `zerops_workflow`, how to discover services, how to treat deploy and verify evidence, and when a task is done. The MCP server exposes the tools; the generated instructions teach the agent the lifecycle. Nothing requires another MCP client to call `zerops_workflow` unless you add equivalent policy yourself. + +Using only the tools is valid. In that setup, your integration owns the sequencing: which services are in scope, when to deploy, what counts as verification, how to read logs or events, and when to stop. Tool-level gates still apply, including explicit service deletion approval and destructive import override acknowledgement. + +## Generated files + +`zcp init` and the remote ZCP workspace create a small amount of agent configuration around the MCP server. + +| File or directory | Created where | Purpose | +| ----------------- | ------------- | ------- | +| `CLAUDE.md` | Remote workspace or local project directory | Claude Code instruction surface. ZCP writes a managed block between `` and ``. User content outside that block is preserved. | +| `.claude/settings.local.json` | Remote workspace or local project directory | Claude Code project settings and ZCP tool permissions for that directory. | +| `.mcp.json` | Local project directory | Project-local MCP server config. It contains the local `zcp` command and the project-scoped `ZCP_API_KEY`; keep it out of git. | +| `~/.claude.json` and SSH config | Remote ZCP workspace | Workspace-level Claude Code and SSH wiring used inside the Zerops-hosted workspace. | +| `.zcp/state/` | Working directory where the MCP server runs | ZCP workflow state and service metadata for that project directory. | + +The managed `CLAUDE.md` block is refreshed by `zcp init` and by the MCP server when the block already exists. Put durable project instructions outside the ZCP markers. Edits inside the managed block are treated as generated content. + +## `.zcp/state` + +`.zcp/state/` is project-scoped state for workflow-aware ZCP use. It is not application source code and should not be committed. + +| Path | What it stores | +| ---- | -------------- | +| `.zcp/state/services/*.json` | Persistent service metadata: known runtime services, local/stage pairing, delivery preference, git-push setup, build integration, and first-deploy stamps. | +| `.zcp/state/sessions/*.json` | Bootstrap, recipe, or packaging workflow sessions. | +| `.zcp/state/work/{pid}.json` | The current agent-process work session: task intent, services in scope, recent deploy attempts, recent verify attempts, and close status. | +| `.zcp/state/registry.json` | Session ownership and lookup data for active workflow state. | +| `.zcp/state/locks/` | Local coordination locks for stateful operations. | + +ZCP does not use `.zcp/state/` as a stale copy of the Zerops project. Service status, logs, events, runtime files, env-var values, and current platform configuration are read from Zerops or from the local filesystem when tools run. Local `.env` files are generated separately and may contain secrets; `.zcp/state/` is metadata for ZCP's own coordination. + +Do not edit `.zcp/state/` by hand during normal work. Use ZCP tools to reset, resume, iterate, or reconfigure workflow state. Deleting it intentionally discards ZCP's local memory of services and delivery setup for that directory; the tools can rediscover live Zerops state, but delivery preferences and workflow history may need to be configured again. + +## Tool-only use + +If you do not want ZCP to guide Claude Code through workflows, keep the MCP connection and remove the ZCP-managed workflow block from `CLAUDE.md`, or keep your own policy outside the ZCP markers. You can also use a different MCP client that never reads that file. + +After that, direct MCP operations still work. The client can call `zerops_discover`, `zerops_logs`, `zerops_events`, `zerops_env`, `zerops_deploy`, `zerops_verify`, and the other tools directly. What you lose is the generated instruction layer that makes the agent plan around live state, use the workflow status, deploy with bounded retries, verify behavior, and report proof or a concrete blocker. + +This is a reasonable advanced setup when you are building your own integration or want narrow operational access. Be explicit about the policy you expect from the agent, and test the integration more carefully because ZCP is no longer steering the whole task lifecycle for you. ## Permission classes @@ -19,7 +67,7 @@ Most users never need operation names. They matter when you configure agent-clie | ------------------ | ----------------------------------------------------------------------------- | | `zerops_discover` | Read services, ports, env-var keys, and current state. | | `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | -| `zerops_events` | Read service-scoped project activity, deploys, builds, scaling, and failures. | +| `zerops_events` | Read service activity, deploys, builds, scaling, and failures. | | `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | | `zerops_knowledge` | Fetch ZCP guidance or platform knowledge for the current state. | | `zerops_process` | Check a known async process; cancel is a mutating action. | @@ -47,13 +95,13 @@ Most users never need operation names. They matter when you configure agent-clie ## Remote and local differences -| Area | Remote setup | Local setup | -| ------------- | --------------------------------------------------- | --------------------------------------------------------------- | -| Files | Runtime files through SSHFS/project containers | Local working directory | -| Deploy source | Project service or batch deploy inside Zerops | Local `workingDir` | -| Dev server | ZCP can run/inspect project-container dev processes | Your local tool owns the dev server | -| Env bridge | Project env is available in the project | `.env` generation resolves project references for local app use | -| Git | Workspace-managed credentials | User's local git credentials | +| Area | Remote setup | Local setup | +| ------------- | ------------------------------------------------ | ------------------------------------------------------------- | +| Files | Runtime files through SSHFS or remote containers | Local working directory | +| Deploy source | Remote service or batch deploy inside Zerops | Local `workingDir` | +| Dev server | ZCP can run or inspect remote dev processes | Your local tool owns the dev server | +| Env bridge | Env vars are already available in Zerops | `.env` generation resolves Zerops references for local use | +| Git | Workspace-managed credentials | User's local git credentials | ## Confirmation gates diff --git a/apps/docs/content/zcp/reference/troubleshooting.mdx b/apps/docs/content/zcp/reference/troubleshooting.mdx index 2062601d7..50b0d23cf 100644 --- a/apps/docs/content/zcp/reference/troubleshooting.mdx +++ b/apps/docs/content/zcp/reference/troubleshooting.mdx @@ -1,15 +1,15 @@ --- title: 'Troubleshooting' -description: 'Recover ZCP sessions by reading current state, failure category, logs, events, token state, VPN state, and local setup artifacts.' +description: 'Recover ZCP sessions from current state, failure category, logs, events, tokens, VPN, and local setup artifacts.' --- -Start recovery from current project state, not from chat memory. +Start recovery from current state, not from chat memory. ```text -Read ZCP status and tell me where this project stands before changing anything. +Read ZCP status and tell me where things stand before changing anything. ``` -That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. ZCP can rebuild the project picture from live services, recent deploys, logs, events, and saved work-session state. +That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. ZCP can rebuild the picture from live services, recent deploys, logs, events, and saved work state. ## Start from the failure category @@ -20,7 +20,7 @@ When a deploy or verify step fails, ZCP should surface a category with the likel | `build` | Build failed before runtime start. | Build logs and `zerops.yaml` build steps. | Fix build commands, dependency manifests, lock files, deploy files, or build resources. Runtime logs will not explain this failure. | | `start` | Build passed, runtime failed to start or crashed. | Prepare logs or runtime logs, whichever the failure names. | Check `run.start`, ports, env references, `prepareCommands`, and whether the process binds `0.0.0.0` rather than `127.0.0.1`. | | `verify` | Runtime exists, but reachability or requested behavior failed. | The failing check, HTTP response, and runtime logs at request time. | Treat it as app behavior or route recovery, not deploy success. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | The transport error and the named network path. | Check VPN for local setup, service status for remote setup, SSH transfer state, DNS/subdomain readiness, or platform timeout. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | The transport error and named network route. | Check VPN for local setup, service status for remote setup, SSH transfer state, DNS/subdomain readiness, or platform timeout. | | `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Structured rejection detail, field path, or API metadata. | Fix the named setup block, env reference, `deployFiles`, service type, or prerequisite. | | `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | The credential surface named in the error. | Replace that credential only: `ZCP_API_KEY`, `GIT_TOKEN`, local git auth, `ZEROPS_TOKEN`, SSH key, or app secret. | | `other` | No known category matched. | Raw reason, service events, and logs for the named phase. | Stop if the same unknown reason repeats; report the raw reason and recent evidence. | @@ -64,24 +64,24 @@ Token problems usually happen at startup. The fix is to replace the token on the | Symptom | Likely cause | Next move | | -------------------------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| `Token accesses N projects; use project-scoped token` | Multi-project or account-wide token. | Generate a token scoped to exactly one project and replace `ZCP_API_KEY`. | -| `Token has no project access` | Token authenticates but reaches no project. | Grant project access or generate a new project-scoped token. | +| `Token accesses N projects; use project-scoped token` | Multi-project or account-wide token. | Generate a single-project token and replace `ZCP_API_KEY`. | +| `Token has no project access` | Token authenticates but reaches no project. | Grant project access or generate a new single-project token. | | `No authentication found: set ZCP_API_KEY or log in with zcli` | No token reached ZCP. | Add `ZCP_API_KEY` under `.mcp.json` `env` for local setup, or check remote service env. | -| API replies with 401 or `AUTH_TOKEN_EXPIRED` | Token expired or was revoked. | Generate a new project-scoped token, replace it, restart the agent. | +| API replies with 401 or `AUTH_TOKEN_EXPIRED` | Token expired or was revoked. | Generate a new single-project token, replace it, restart the agent. | | GitHub Actions deploy fails on auth | `ZEROPS_TOKEN` missing/wrong. | Set a Zerops API token as the repository secret named `ZEROPS_TOKEN`. It is not a GitHub token. | Full credential model: [Tokens and credentials](/zcp/security/tokens-and-project-access). ## Local setup symptoms -These apply to local setup. Remote setup is already on the project-private network and does not need VPN from your laptop. +These apply to local setup. Remote setup is already on the private network and does not need VPN from your laptop. | Symptom | Likely cause | Next move | | --------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | Local app cannot reach `db`, `redis`, `cache`, or storage hostname | VPN is down or dropped after sleep/network change. | Run `zcli vpn up ` again. | -| `no route to host` or connection refused for managed-service hostname | Same network path issue. | Bring VPN up, then retry. If network works, regenerate `.env`. | -| Local app reads stale project credentials | `.env` is a snapshot. | Ask ZCP to regenerate the `.env` after project env changes. | -| Re-running `zcp init` made ZCP disappear from the agent | `.mcp.json` was rewritten without `ZCP_API_KEY`. | Re-add the `env` block and restart the agent from the project root. | +| `no route to host` or connection refused for managed-service hostname | Same network access issue. | Bring VPN up, then retry. If network works, regenerate `.env`. | +| Local app reads stale credentials | `.env` is a snapshot. | Ask ZCP to regenerate the `.env` after env changes. | +| Re-running `zcp init` made ZCP disappear from the agent | `.mcp.json` was rewritten without `ZCP_API_KEY`. | Re-add the `env` block and restart the agent from the app directory. | | Agent does not list the `zerops` MCP server | Agent launched from the wrong directory or config not loaded. | Quit and relaunch from the directory containing `.mcp.json`. | | `zcp` is not found after install | Install path is not on `PATH`. | Add `~/.local/bin` or the install target to your shell `PATH`, then restart the shell/agent. | @@ -108,7 +108,7 @@ For manual takeover, read evidence in this order: Stop the loop when the agent cannot make progress with new evidence, needs a missing credential, is about to delete or replace a service, or the target runtime/stage is ambiguous. -A repeated retry with the same evidence is not recovery. Ask the agent for the category, evidence read, fixes attempted, and the next human decision. +A repeated retry with the same evidence is not recovery. Ask the agent for the category, evidence read, fixes attempted, and next human decision. Before destructive recovery, read service-scoped events, logs, deploy/verify result, and git history when delivery uses git-push. @@ -117,4 +117,4 @@ Before destructive recovery, read service-scoped events, logs, deploy/verify res - [Build with ZCP](/zcp/workflows/build-with-zcp) - [Workflow terms](/zcp/reference/agent-workflow) - [Tokens and credentials](/zcp/security/tokens-and-project-access) -- [Set up local ZCP](/zcp/setup/local-agent-bridge) +- [How to run ZCP on your machine](/zcp/setup/local-agent-bridge) diff --git a/apps/docs/content/zcp/security/production-policy.mdx b/apps/docs/content/zcp/security/production-policy.mdx index 0571adcf0..b71726d2b 100644 --- a/apps/docs/content/zcp/security/production-policy.mdx +++ b/apps/docs/content/zcp/security/production-policy.mdx @@ -1,11 +1,11 @@ --- 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." +description: 'Keep ZCP in development or staging. Promote verified work to production through CI, release tooling, or a deliberate human action.' --- -Rule: use ZCP in development or staging projects. Production should be a separate Zerops project without a `zcp` service, and production deploys should come from CI, a release pipeline, or a deliberate human `zcli` push using production-scoped credentials. +Use ZCP in development or staging. Keep production in a separate Zerops project without a `zcp` service. Production deploys should come from CI, a release pipeline, or a deliberate human `zcli` push using production credentials. -Zerops does not prevent you from adding a `zcp` service to production. The policy exists because ZCP gives a coding agent project-scoped operational access. In production, that is the wrong blast radius for normal app development. +Zerops does not prevent you from adding `zcp` to production. The policy exists because ZCP gives a coding agent operational access. In production, that is the wrong blast radius for normal app development. ## Recommended project layout @@ -14,11 +14,11 @@ Zerops does not prevent you from adding a `zcp` service to production. The polic | Development / staging project | `zcp` service, agent workspace, dev runtime, optional stage runtime, non-production managed services | Agent plus humans | | Production project | Production runtimes, production managed services, production env values, production backups and scaling | CI/release process plus humans | -The two projects use different Zerops tokens. A token scoped to the development project cannot reach production. A production token should not be placed in the development `zcp` service or a local `.mcp.json` used by the agent. +The two projects use different Zerops tokens. A development token cannot reach production. A production token should not be placed in the development `zcp` service or a local `.mcp.json` used by the agent. ## Why production is a separate project -- **The project is the security boundary.** ZCP binds to one project at startup. Keeping production in another project prevents a development agent from touching production by accident. +- **The Zerops project is the security boundary.** ZCP binds to one project at startup. Keeping production separate prevents a development agent from touching it by accident. - **Secrets stay clean.** Production env values, database credentials, object-storage keys, and third-party secrets stay in the production project. - **Operational policies can differ.** Production often needs HA services, stronger backup retention, stricter scaling, alerts, and release approvals. Development can stay cheaper and more flexible. - **Audit trails stay readable.** Development experiments and agent retries do not mix with production deploy evidence. @@ -27,27 +27,27 @@ This separation matters even when the same source repository deploys to both pro ## What stage proves -Stage is the production-like rehearsal inside the development/staging project. It is where the agent proves the change before promotion leaves the ZCP project. +Stage is the production-like rehearsal inside development or staging. It is where the agent proves the change before promotion leaves the ZCP loop. Use stage to match production where it matters: - same runtime family and version, - same managed service types, - same build and start command pattern, -- same deploy path, +- same deploy route, - behavior checks against a real running service. -Stage is not production. It should use non-production data and non-production secrets. A green stage means the change is ready for the production release path, not that the agent should deploy to production itself. +Stage is not production. It should use non-production data and non-production secrets. A green stage means the change is ready for release, not that the agent should deploy to production itself. -## Promotion path +## Promotion -After stage verifies, ZCP's job is done for that change. Promotion to production happens outside the agent loop: +After stage verifies, ZCP's job is done for that change. Promotion happens outside the agent loop: - CI deploys a merged commit or release tag to the production project. - A release pipeline runs `zcli push` with a production-scoped `ZEROPS_TOKEN`. - A human runs `zcli push` against the production project with production credentials. -The agent can prepare the handoff by pushing code, summarizing verification evidence, and naming the runtime and URL it verified. It should not bridge the development project to the production project. +The agent can prepare the handoff by pushing code, summarizing verification evidence, and naming the runtime and URL it verified. It should not bridge development to production. Broader CI/CD reference: [CI/CD with Zerops](/guides/ci-cd). @@ -58,7 +58,7 @@ Broader CI/CD reference: [CI/CD with Zerops](/guides/ci-cd). | Development `ZCP_API_KEY` | Never grants production access. Keep it scoped to the development/staging project. | | Production `ZEROPS_TOKEN` | Store only in the production CI/release secret store. Do not place it in the development `zcp` service. | | Agent subscription or model API key | May be used by the agent, but it does not grant Zerops production access by itself. | -| Git credentials | May push source changes, but production deploy authority should still be controlled by the release path. | +| Git credentials | May push source changes, but production deploy authority should stay with the release process. | If a production deploy fails, investigate in the production project with production logs, events, backups, and CI output. Do not attach the development agent directly to production as a shortcut. @@ -66,11 +66,11 @@ If a production deploy fails, investigate in the production project with product | Rule | Why it matters | | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -| Keep the production project without a `zcp` service | Production should not host an agent workspace with project-scoped operational access. | +| Keep the production project without a `zcp` service | Production should not host an agent workspace with operational access. | | Keep production tokens out of local `.mcp.json` | Local agents should operate development or staging projects, not production. | -| Keep production `ZEROPS_TOKEN` in the release secret store | Development tooling should not deploy to production outside the release path. | +| Keep production `ZEROPS_TOKEN` in the release secret store | Development tooling should not deploy to production outside the release process. | | Treat stage proof as release evidence, not production approval | Stage is the rehearsal; production approval is a separate release decision. | -| Promote through CI, release tooling, or a deliberate human action | Production execution should use the production credential boundary. | +| Promote through CI, release tooling, or a deliberate human action | Production execution should use production credentials. | ## Acceptable agent involvement @@ -79,15 +79,15 @@ The agent can still help before production promotion: - make the code change in development, - deploy and verify dev/stage runtimes, - produce the URL, endpoint result, or UI proof it verified, -- push or prepare the code path your team uses for review, +- push or prepare the branch your team uses for review, - summarize release notes and known blockers for the human or CI handoff. -ZCP's involvement stops at the handoff. Production execution belongs to the production release path. +ZCP's involvement stops at the handoff. Production execution belongs to the release process. ## Next steps -- [Trust model](/zcp/security/trust-model) - the project boundary that makes this policy enforceable. -- [Tokens and credentials](/zcp/security/tokens-and-project-access) - project-scoped token handling. -- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare development setup paths. +- [Trust model](/zcp/security/trust-model) - the boundary that makes this policy enforceable. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - token handling. +- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare development setups. - [Build with ZCP](/zcp/workflows/build-with-zcp) - the dev/stage loop this policy assumes. - [Backup](/features/backup) - production recovery outside ZCP. 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 dbbc87dbf..81c3fd5a7 100644 --- a/apps/docs/content/zcp/security/tokens-and-project-access.mdx +++ b/apps/docs/content/zcp/security/tokens-and-project-access.mdx @@ -1,11 +1,11 @@ --- title: 'Tokens and credentials' -description: 'Project-scoped Zerops tokens, git credentials, and the confirmation gates ZCP enforces before destructive actions.' +description: 'ZCP_API_KEY, agent credentials, git credentials, CI tokens, and destructive-action confirmations.' --- -ZCP uses a Zerops API token to operate exactly one project. The important rule is simple: `ZCP_API_KEY` is a project credential for ZCP, not an agent login, not a git token, and not a general account token. +ZCP uses a Zerops API token to operate exactly one project. The important rule is simple: `ZCP_API_KEY` is the Zerops credential for ZCP, not an agent login, not a git token, and not a general account token. -Remote setup gets `ZCP_API_KEY` from the Zerops platform. Local setup reads it from `.mcp.json`. In both paths, ZCP validates the token at startup and refuses tokens that resolve to no project or multiple projects. +Remote setup gets `ZCP_API_KEY` from Zerops. Local setup reads it from `.mcp.json`. In both setups, ZCP validates the token at startup and refuses tokens that resolve to no project or multiple projects. ## Credential map @@ -14,13 +14,13 @@ Remote setup gets `ZCP_API_KEY` from the Zerops platform. Local setup reads it f | `ZCP_API_KEY` | ZCP against one Zerops project | Remote: `zcp` service env injected by Zerops. Local: `.mcp.json` env block. | | Agent login or model API key | The coding agent itself | The bundled agent in remote setup, or your local agent client. | | `GIT_TOKEN` | Git push from remote setup to a git provider | Secret env var on the `zcp` service when remote git-push delivery needs it. | -| `ZEROPS_TOKEN` | `zcli` in GitHub Actions or external CI | Separate project-scoped Zerops token stored in the CI/release secret store. | +| `ZEROPS_TOKEN` | `zcli` in GitHub Actions or external CI | Separate Zerops delivery token stored in the CI/release secret store. | Keeping these names separate prevents most setup and delivery failures. ## Recommended `ZCP_API_KEY` shape -Use a Zerops API token with **Custom access per project**, scoped to exactly one project, with **Full access** for normal agent work. +Use a Zerops API token with **Custom access per project**, exactly one selected project, and **Full access** for normal agent work. Read-only tokens can authenticate, but they fail as soon as the agent needs to deploy, write env vars, restart services, scale, or change public access. Account-wide or multi-project tokens are refused before the agent can operate. @@ -42,7 +42,7 @@ ZCP validates token shape at startup. | Token shape | What happens | Fix | | ----------------------------------- | ------------------------------------------- | -------------------------------------------------------------------- | | Account-wide or multi-project token | ZCP refuses to start. | Generate a token scoped to exactly one project. | -| Token with no project access | ZCP refuses to start. | Grant one project or create a new project-scoped token. | +| Token with no project access | ZCP refuses to start. | Grant one project or create a new single-project token. | | Expired or revoked token | ZCP refuses to start or receives HTTP 401. | Replace the token and restart the agent or ZCP process. | | Read-only project token | Startup may pass, but mutations fail later. | Use full access for normal agent work, or expect read-only behavior. | @@ -55,7 +55,7 @@ Common messages: | `No authentication found: set ZCP_API_KEY or log in with zcli` | The `zcp` process did not receive a usable token. | | `AUTH_TOKEN_EXPIRED` or HTTP 401 | The token expired, was revoked, or is invalid. | -For ZCP setup, provide `ZCP_API_KEY`. `zcli` login is a diagnostic fallback, not the normal agent setup path. +For ZCP setup, provide `ZCP_API_KEY`. `zcli` login is a diagnostic fallback, not the normal agent setup. ## Where the token lives - remote vs local {#where-the-token-lives--remote-vs-local} @@ -64,7 +64,7 @@ ZCP reads `ZCP_API_KEY` from its process environment at startup. It does not wri | Setup | Where `ZCP_API_KEY` comes from | Who provisions it | | -------------------------------------------- | ------------------------------------------------------- | -------------------------------------------------------- | | [Remote setup](/zcp/setup/hosted-workspace) | The `zcp` service environment | Zerops injects it automatically when the service starts. | -| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You add it after `zcp init`. | +| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the app directory | You add it after `zcp init`. | In remote setup, do not hand-edit `ZCP_API_KEY`. Replace or rotate it through the Zerops-managed surface so the service keeps the intended project boundary. @@ -77,28 +77,28 @@ In local setup, `zcp init` writes a token-less `.mcp.json`. Add the token manual "command": "zcp", "args": ["serve"], "env": { - "ZCP_API_KEY": "" + "ZCP_API_KEY": "" } } } } ``` -Add `.mcp.json` to `.gitignore`. Each project directory should have its own file and its own project token. Switching projects means switching directories, not editing one shared credential. +Add `.mcp.json` to `.gitignore`. Each app directory should have its own file and token. Switching projects means switching directories, not editing one shared credential. ## Agent credentials are separate The bundled agent in remote setup may ask you to sign in or provide a model API key. That is not `ZCP_API_KEY`. -Zerops wires the agent to the project through ZCP. It does not provide your model subscription, store your agent login, or rotate your agent provider credentials. Treat the agent account exactly as you would outside Zerops. +Zerops wires the agent to ZCP. It does not provide your model subscription, store your agent login, or rotate your agent provider credentials. Treat the agent account exactly as you would outside Zerops. ## Git and CI credentials `GIT_TOKEN` matters only when remote setup pushes to a git remote. It authorizes git provider access from the `zcp` service. In local setup, your local git CLI uses your normal SSH key or credential helper, so ZCP does not need `GIT_TOKEN`. -`ZEROPS_TOKEN` is a Zerops API token used by GitHub Actions or another CI system when that system runs `zcli` against Zerops. It is not a GitHub token. Use a separate project-scoped delivery token so ZCP sessions and CI/release workflows can be named, rotated, and audited independently. +`ZEROPS_TOKEN` is a Zerops API token used by GitHub Actions or another CI system when that system runs `zcli` against Zerops. It is not a GitHub token. Use a separate delivery token so ZCP sessions and CI/release workflows can be named, rotated, and audited independently. -For production delivery, `ZEROPS_TOKEN` should be scoped to the production project and stored only in the production CI or release secret store. +For production delivery, `ZEROPS_TOKEN` should reach production only and live only in the production CI or release secret store. With GitHub CLI, the secret shape is: @@ -114,12 +114,12 @@ Rotate in the Zerops dashboard, then update the surface that consumes the token. | Surface | Rotation step | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -| Remote `ZCP_API_KEY` | Reconfigure or redeploy the remote workspace through the dashboard-managed path, then restart the `zcp` service so the process gets the new value. | +| Remote `ZCP_API_KEY` | Reconfigure or redeploy the remote workspace through the dashboard-managed flow, then restart the `zcp` service so the process gets the new value. | | Local `ZCP_API_KEY` | Paste the new token into `.mcp.json`, then restart the local agent client. | | `ZEROPS_TOKEN` in CI | Replace the repository or CI secret. The next workflow run uses the new value. | | `GIT_TOKEN` | Replace the git-provider credential stored for remote setup. | -Rotation is picked up on next process start or workflow run, not in the middle of a live agent session. +Rotation is picked up on the next process start or CI run, not in the middle of a live agent session. ## What ZCP enforces for destructive actions @@ -130,7 +130,7 @@ A valid token does not remove every guardrail. ZCP adds explicit confirmation fo | Service deletion | Requires explicit approval in the same conversation, including the service name. Remote setup also blocks deleting the `zcp` service it is running in. | | Wholesale service replacement after failed deploy history | The first request refuses, surfaces what would be replaced, and requires the agent to read failure evidence before asking you to confirm. | -Deploys, env changes, lifecycle actions, restarts, scaling, and public-access changes do not get an additional ZCP-specific confirmation gate. They are normal project operations for a full-access token and should be reviewed through service events, logs, verification output, and team policy. +Deploys, env changes, lifecycle actions, restarts, scaling, and public-access changes do not get an additional ZCP-specific confirmation gate. They are normal operations for a full-access token and should be reviewed through service events, logs, verification output, and team policy. Approval from a previous chat does not carry forward. A new conversation needs a new approval. @@ -148,15 +148,15 @@ Threat model and boundaries: [Trust model](/zcp/security/trust-model). - **Account-wide full-access tokens are refused.** ZCP needs one project, not a broad account credential. - **`zcp init` regenerates `.mcp.json`.** Re-add `ZCP_API_KEY` after rerunning it. -- **`GIT_TOKEN` is not `ZCP_API_KEY`.** One authorizes git provider access; the other authorizes Zerops project operations. +- **`GIT_TOKEN` is not `ZCP_API_KEY`.** One authorizes git provider access; the other authorizes Zerops operations. - **`ZEROPS_TOKEN` in GitHub Actions is not a GitHub PAT.** It is a Zerops API token for `zcli`. - **A rotated token needs a restart.** The live ZCP process keeps the old environment value until it starts again. - **A successful confirmation is still destructive.** Backups, git history, and service events are your recovery evidence; ZCP does not auto-rollback a confirmed deletion or replacement. ## Related -- [Trust model](/zcp/security/trust-model) - the project boundary this page enforces. -- [Use remote workspace](/zcp/setup/hosted-workspace) - automatic token injection. -- [Set up local ZCP](/zcp/setup/local-agent-bridge) - local `.mcp.json` token setup. +- [Trust model](/zcp/security/trust-model) - the access boundary this page enforces. +- [What remote workspace gives you](/zcp/setup/hosted-workspace) - automatic token injection. +- [How to run ZCP on your machine](/zcp/setup/local-agent-bridge) - local `.mcp.json` token setup. - [Production boundary](/zcp/security/production-policy) - why production gets separate credentials. - [GitHub integration](/references/github-integration) - CI secret usage with Zerops. diff --git a/apps/docs/content/zcp/security/trust-model.mdx b/apps/docs/content/zcp/security/trust-model.mdx index 60f63fe03..85021341d 100644 --- a/apps/docs/content/zcp/security/trust-model.mdx +++ b/apps/docs/content/zcp/security/trust-model.mdx @@ -1,11 +1,11 @@ --- title: 'Trust model' -description: 'ZCP gives a coding agent operational access scoped to one Zerops project. Remote and local setup set different blast radii.' +description: 'How ZCP limits Zerops access, and how remote and local setup change the surrounding blast radius.' --- -The ZCP trust model starts with one rule: one ZCP process operates one Zerops project. The token decides the project boundary at startup, and ZCP refuses tokens that resolve to no project or multiple projects. +The trust model starts with one rule: one ZCP process operates one Zerops project. `ZCP_API_KEY` decides that boundary at startup, and ZCP refuses tokens that resolve to no project or multiple projects. -That boundary is strong on the Zerops side. It does not make the agent harmless. A project-scoped token can still deploy, change env vars, restart services, read logs, scale services, and change public access inside that project when Zerops permissions allow it. Treat it like an operations credential for one project. +That boundary is strong on the Zerops side. It does not make the agent harmless. A valid token can still deploy, change env vars, restart services, read logs, scale services, and change public access when Zerops permissions allow it. Treat it like an operations credential. ## Boundary summary @@ -14,25 +14,25 @@ That boundary is strong on the Zerops side. It does not make the agent harmless. | What project can ZCP see? | Exactly one project resolved from `ZCP_API_KEY` at startup. | | What can ZCP change? | Whatever Zerops RBAC grants that token inside the project. | | What is outside reach? | Other projects, organization-wide settings, billing, and any operation Zerops RBAC rejects. | -| What network can remote setup reach? | The project's private network from inside the `zcp` service. | -| What network can local setup reach? | The Zerops API directly, plus project-private services only when your laptop VPN is up. | +| What network can remote setup reach? | Private services from inside the `zcp` service. | +| What network can local setup reach? | The Zerops API directly, plus private services only when your laptop VPN is up. | | Who owns the agent login? | You. The agent subscription or model API key is separate from `ZCP_API_KEY`. | Zerops [RBAC](/features/rbac) remains the authority. ZCP does not bypass platform permissions; it exposes project operations to the agent only through the token it was given. ## Remote and local blast radius -Remote setup and local setup share the same project boundary, but not the same surroundings. +Remote setup and local setup share the same Zerops boundary, but not the same surroundings. | Area | Remote setup | Local setup | | -------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------- | | Agent location | Inside the `zcp@1` service when **Include Coding Agent** is enabled | On your machine through your editor or CLI agent | | Files visible to the agent | Workspace files and mounted runtime files | Local files and anything your client permits | -| Private service access | Project-private network without laptop VPN | `zcli vpn up ` from your machine | +| Private service access | Private network without laptop VPN | `zcli vpn up ` from your machine | | Local machine exposure | No direct access to your laptop | Agent client runs as your local user | | Best safety posture | Keep ZCP in a development/staging project and avoid mounting unrelated files | Restrict client permissions, shell access, and filesystem scope | -Remote setup is project-contained by design. Local setup is supervise-the-client by design. Either can be the right choice, but local setup requires more attention to your agent client's local permissions. +Remote setup is contained inside Zerops by design. Local setup is supervise-the-client by design. Either can be right, but local setup requires more attention to your agent client's local permissions. ## Credential ownership @@ -40,7 +40,7 @@ There are three separate credential surfaces people often mix together: | Credential | Owner | Purpose | | ----------------------------------- | ---------------------------- | ------------------------------------------------------ | -| `ZCP_API_KEY` | Zerops project token | Lets ZCP operate one Zerops project. | +| `ZCP_API_KEY` | Zerops token | Lets ZCP operate one Zerops project. | | Agent subscription or model API key | You / the agent provider | Lets the coding agent run. Zerops does not provide it. | | Git or CI credentials | You / your repository system | Lets finished work push to git or deploy through CI. | @@ -48,9 +48,9 @@ Remote setup injects `ZCP_API_KEY` into the `zcp` service. Local setup reads it Details: [Tokens and credentials](/zcp/security/tokens-and-project-access). -## What a project-scoped agent can do +## What the token lets the agent do -Inside the project, the agent can perform normal operational work when the token has permission: +The agent can perform normal operational work when the token has permission: - discover runtime and managed services, - create or adjust services when the task requires it, @@ -59,21 +59,21 @@ Inside the project, the agent can perform normal operational work when the token - read build logs, runtime logs, and service events, - restart, reload, stop, start, or scale services, - enable or disable public access, -- prepare delivery paths such as git-push or CI handoff. +- prepare delivery such as git push or CI handoff. This is why development and staging projects are the right place for ZCP. Production should be a separate project without a `zcp` service; see [Production boundary](/zcp/security/production-policy). ## Remote setup specifics - **`zcp` service is not the app.** It is the workspace and control surface. Deploys target app runtimes, not the `zcp` service. -- **The service has project-level operational reach.** A terminal in the `zcp` service can use the same project-scoped access as the agent. Review the dashboard's additional changes before deploying the service; they are what give the workspace its project-scoped operating surface. -- **Network reach is broader than file reach.** The workspace can reach project-private services, but it only sees runtime files that are mounted into it. +- **The service has operational reach.** A terminal in the `zcp` service can use the same ZCP access as the agent. Review the dashboard's additional changes before deploying the service; they are what give the workspace its operating surface. +- **Network reach is broader than file reach.** The workspace can reach private services, but it only sees runtime files that are mounted into it. - **Do not hand-edit `ZCP_API_KEY`.** Remote setup gets the value from Zerops. Manual replacement can break the intended one-project boundary. ## Local setup specifics -- **The agent inherits local reach.** ZCP is project-scoped, but the local agent client can read files, run commands, and use credentials allowed by your client settings. -- **Each local directory has its own config.** `.mcp.json` and `.zcp/state/` belong to one project directory. Launching from the wrong directory can connect the wrong project or no project. +- **The agent inherits local reach.** ZCP is limited to one project, but the local agent client can read files, run commands, and use credentials allowed by your client settings. +- **Each local directory has its own config.** `.mcp.json` and `.zcp/state/` belong to one app directory. Launching from the wrong directory can connect the wrong token or no token. - **VPN is outside ZCP authority.** Bringing up Zerops VPN needs your operating-system approval. ZCP cannot grant that for the agent. - **`.env` files are snapshots.** They contain real project credentials and should stay out of git. diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index 7bc981984..fe3b700eb 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -3,12 +3,12 @@ title: 'Remote or local ZCP setup' description: 'Where the ZCP workspace runs: inside Zerops or on your machine.' --- -Remote setup is the default for most ZCP work. Use local setup when the agent specifically needs to run next to local files, local data, local tools, or a local-only agent client. +Remote setup is the default for most ZCP work. Use local setup when the agent needs your local files, data, tools, editor, or a local-only agent client. -Both paths use the same `zcp` binary and the same ZCP model: one Zerops project, live project state, project-scoped operations, and an agent that can finish with proof or a blocker. The choice is where the agent workspace lives. +Both setups use the same `zcp` binary and the same model: live state, bounded operations, and a finish line of proof or a blocker. The choice is where the agent workspace lives. :::tip Recommended default -**Remote setup** gives you a project-contained agent workspace with clean isolation inside the Zerops project, no local install, project-private networking, and a preconfigured Claude Code + browser VS Code environment when enabled. +**Remote setup** gives you an agent workspace inside Zerops, no local install, private networking, and preconfigured Claude Code + Browser VS Code when enabled. ::: ## The short version @@ -17,65 +17,64 @@ Both paths use the same `zcp` binary and the same ZCP model: one Zerops project,

Local setup

- The agent runs from your machine. Use it when local files, local data, - your desktop editor, local git credentials, or a specific local agent - client should stay in charge. + The agent runs from your machine. Use it when files, data, desktop editor, + git credentials, or a specific local agent client should stay local.

  • Install zcp locally.
  • Run zcp init in the working directory.
  • -
  • Add a project-scoped token.
  • +
  • Add a Zerops token for one project.
  • Use zcli vpn up for private service access.
- Set up local ZCP → + Run ZCP locally →
-Remote setup usually costs one extra workspace service. Local setup avoids that workspace service, but deploy targets and managed services still cost while running. For most teams, the decision should be about where the agent should safely and ergonomically work, not about the small workspace cost. +Remote setup usually adds one small workspace service. Local setup avoids that service, but deploy targets and managed services still cost while running. For most teams, the real decision is where the agent should safely and ergonomically work. :::info Public preview Remote setup and local setup are both public preview. Local setup has more moving parts and may change faster: binary install path, `.mcp.json`, and `zcp init` artifacts are still settling between releases. ::: -## What changes between the paths +## What changes between setups | | Remote setup | Local setup | | ---------------------- | -------------------------------------------------------------- | -------------------------------------------- | -| `zcp` process | Runs in the Zerops project in a `zcp@1` service. | Runs on your machine. | -| Agent process | Runs in the project workspace when bundled or installed there. | Runs in your local editor or CLI. | +| `zcp` process | Runs in Zerops as a `zcp@1` service. | Runs on your machine. | +| Agent process | Runs in the remote workspace when bundled or installed there. | Runs in your local editor or CLI. | | Files the agent edits | Workspace files and mounted runtime files. | Files on your machine. | -| Private service access | Project-private network from inside Zerops. | Zerops VPN from your machine. | -| Token storage | Platform-injected into the workspace service. | `.mcp.json` in the local project directory. | +| Private service access | Private Zerops network from the workspace. | Zerops VPN from your machine. | +| Token storage | Platform-injected into the workspace service. | `.mcp.json` in the local app directory. | | Git credentials | Configured inside the workspace service. | Your local git credentials. | -| Safety posture | Agent shell permissions are contained in the project service. | Agent shell permissions affect your machine. | +| Safety posture | Agent shell permissions are contained in the workspace service. | Agent shell permissions affect your machine. | ## Starting points Remote setup can start from: - **Recipe with AI Agent environment.** A guided path for a known stack. A recipe creates app services, managed services, and the `zcp@1` workspace together. -- **New project with ZCP enabled.** Start from a blank or custom project and add ZCP during project creation. -- **Existing development or staging project.** Add the `zcp` workspace next to services that already exist. Do not add ZCP to a production project; promote verified work through your release path instead. +- **New Zerops setup with ZCP enabled.** Start from blank services or a custom stack and add ZCP during creation. +- **Existing development or staging setup.** Add the `zcp` workspace next to services that already exist. Do not add ZCP to production; promote verified work through your release process instead. Local setup can start from: - **Empty local directory.** The agent creates the app structure from a product request and uses ZCP to select or create Zerops services. -- **Existing project directory.** The app code, editor setup, local data, and git credentials already live on your machine. +- **Existing app directory.** The app code, editor setup, local data, and git credentials already live on your machine. - **Recipe prepared for local setup.** The recipe creates the Zerops service baseline, while the agent and files stay local. ## Runtime layout is separate @@ -89,28 +88,28 @@ The project can still use: - a single app runtime, - local files linked to a stage target. -That choice belongs to normal app work: [Build with ZCP](/zcp/workflows/build-with-zcp#choose-the-runtime-layout). +That choice belongs to app work: [Build with ZCP](/zcp/workflows/build-with-zcp#choose-the-runtime-layout). -Stage is not production. Keep production in a separate Zerops project and promote work through your release path; see [Production boundary](/zcp/security/production-policy). +Stage is not production. Keep production in a separate Zerops project and promote work through your release process; see [Production boundary](/zcp/security/production-policy). ## Switching later -You can change setup path later without changing the ZCP project boundary. +You can change setup later. -- Start remote for evaluation, then move local when local files or tools should own the workflow. -- Start local for a repo-first project, then add remote setup when you want a project-contained agent workspace. -- Keep both paths only when your team understands which source tree is authoritative for deploys. +- Start remote for evaluation, then move local when files or tools on your machine should own the workflow. +- Start local for a repo-first app, then add remote setup when you want an agent workspace inside Zerops. +- Keep both setups only when your team understands which source tree is authoritative for deploys. Before switching, make the handoff explicit: - Commit, push, or otherwise preserve the source tree that currently owns deploys. -- Choose one deploy source for the next task: the remote workspace/mounted runtime files, or the local project directory. +- Choose one deploy source for the next task: the remote workspace, mounted runtime files, or the local app directory. - Do not edit mounted runtime files and a local repo in parallel unless you have a merge plan. - Regenerate local `.env` snapshots after project env changes. - Relink the local deploy target if the runtime hostname changed. ## Next steps -- [Use remote workspace](/zcp/setup/hosted-workspace) -- [Set up local ZCP](/zcp/setup/local-agent-bridge) +- [What remote workspace gives you](/zcp/setup/hosted-workspace) +- [How to run ZCP on your machine](/zcp/setup/local-agent-bridge) - [Trust model](/zcp/security/trust-model) diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index 444991874..d22a6d630 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -1,44 +1,44 @@ --- -title: 'Use remote workspace' -description: 'Use the project-contained zcp@1 workspace with Claude Code, browser VS Code, and project-scoped ZCP access.' +title: 'What remote workspace gives you' +description: 'What the zcp@1 workspace provides: Claude Code, Browser VS Code, private networking, and ZCP access inside Zerops.' --- -Remote setup gives you an agent workspace inside the Zerops project. +A remote workspace puts the agent, terminal, and optional browser IDE inside Zerops, next to private networking and ZCP access. -The `zcp@1` service runs the same `zcp` binary used by local setup, but the agent, terminal, and optional browser IDE run in a clean project service instead of on your laptop. That is the main advantage: Zerops can provide a low-friction agent workspace with project-private networking and broader shell permissions while keeping that blast radius inside the project. +The `zcp@1` service runs the same `zcp` binary used by local setup, but broad shell permissions and private service access stay in a clean Zerops service instead of on your laptop. -Use this path when you want a project-contained workspace, a safer default boundary for broad agent permissions, or a preconfigured environment with Claude Code and browser VS Code. +Use remote setup when you want the default workspace, a safer boundary for broad agent permissions, or a preconfigured environment with Claude Code and Browser VS Code. App code still deploys to your app runtime services. The `zcp` service is the workspace and control surface; it is not the application runtime. -## What you get +## What it includes -- **`zcp@1` workspace service.** Runs ZCP inside the project and gives the agent project-scoped operations. -- **Platform-injected `ZCP_API_KEY`.** Zerops injects the project token into the workspace; you normally do not set it by hand. -- **Bundled Claude Code when enabled.** The **Include Coding Agent** option installs and preconfigures Claude Code. You authenticate with your own Claude subscription login or API key. More bundled agents are planned. +- **`zcp@1` workspace service.** Runs ZCP inside Zerops and gives the agent bounded operations. +- **Platform-injected `ZCP_API_KEY`.** Zerops injects the token into the workspace; you normally do not set it by hand. +- **Bundled Claude Code when enabled.** The **Include Coding Agent** option installs and preconfigures Claude Code. You authenticate with your own Claude subscription login or API key. - **Browser VS Code when enabled.** The **Cloud IDE** option gives you a browser editor, terminal, and a place to supervise or take over the agent session. -- **Project-private networking.** The workspace can reach managed services by hostname without laptop VPN. +- **Private networking.** The workspace can reach managed services by hostname without laptop VPN. - **Open workspace model.** You can add other agent CLIs, private MCP servers, helper processes, dotfiles, package installs, or a derived team image. -For the local alternative, see [Set up local ZCP](/zcp/setup/local-agent-bridge). For the tradeoffs, see [Remote or local ZCP setup](/zcp/setup/choose-workspace). +For the local alternative, see [How to run ZCP on your machine](/zcp/setup/local-agent-bridge). For the tradeoffs, see [Remote or local ZCP setup](/zcp/setup/choose-workspace). -## Choose a starting path +## Choose a starting point ### First-time trial -Use the [Quickstart](/zcp/quickstart) when you want the guided recipe path. It covers the recipe catalog, **AI Agent** environment, Claude Code authentication, Browser VS Code, first product prompt, and proof. +Use the [Quickstart](/zcp/quickstart) when you want the guided recipe route. It covers the recipe catalog, **AI Agent** environment, Claude Code authentication, Browser VS Code, first product prompt, and proof. After provisioning, continue with a product prompt in [Build with ZCP](/zcp/workflows/build-with-zcp). ### Recipe with AI Agent environment -Use this when you want a guided stack baseline for a new development or staging project. A recipe with an **AI Agent** environment creates app services, managed services, and the `zcp@1` workspace together. +Use this when you want a guided stack baseline for development or staging. A recipe with an **AI Agent** environment creates app services, managed services, and the `zcp@1` workspace together. -Keep **Coding Agent** enabled when you want bundled Claude Code. Keep **Cloud IDE** enabled when you want Browser VS Code in the project. +Keep **Coding Agent** enabled when you want bundled Claude Code. Keep **Cloud IDE** enabled when you want Browser VS Code. -### New project with ZCP enabled +### New Zerops setup with ZCP enabled -Use this when you want a blank or custom project. +Use this when you want blank services or a custom stack. 1. Open [Add new project](https://app.zerops.io/dashboard/project-add). 2. Enter the project name, region, and tags. @@ -47,15 +47,15 @@ Use this when you want a blank or custom project. 5. Keep **Cloud IDE** enabled if you want browser VS Code. 6. Create the project. -This gives you a project with the `zcp@1` workspace. App runtimes and managed services may still be created later by normal ZCP app work. +This gives you the `zcp@1` workspace. App runtimes and managed services may still be created later by normal ZCP app work. -### Existing development or staging project +### Existing development or staging setup -Use this when a development or staging project already has runtime or managed services. Do not add `zcp` to a production project; create or use a separate development/staging project and promote verified work through the release path. +Use this when runtime or managed services already exist in development or staging. Do not add `zcp` to production; promote verified work through your release process. -Add a `zcp` service from the project dashboard the same way you add another Zerops service. The workspace appears next to the existing services and receives project-scoped ZCP access from the platform. +Add a `zcp` service from the dashboard the same way you add another Zerops service. The workspace appears next to the existing services and receives ZCP access from the platform. -The agent should still read project state before changing anything. Existing services are context, not automatic instructions for the next task. +The agent should still read current state before changing anything. Existing services are context, not instructions for the next task. ## Open the workspace @@ -66,7 +66,7 @@ For a returning remote workspace, open the existing `zcp` service: 3. Use **Browser VS Code** when **Cloud IDE** is enabled. 4. Complete the Claude Code login or API-token flow if prompted. -After authentication, Claude Code is connected to this project's ZCP service and ready to work from the browser VS Code terminal. If the workspace was already authorized, use this same path to resume or supervise work. +After authentication, Claude Code is connected to ZCP and ready to work from the Browser VS Code terminal. If the workspace was already authorized, use the same route to resume or supervise work. First load can take a minute while the image, Cloud IDE, and bundled agent finish starting. If the page opens before the service is ready, wait until the service is running and reload. @@ -76,13 +76,13 @@ Browser VS Code is the quickest entry point, not the only one. Editors that support remote development can connect to the workspace or runtime services over SSH, depending on how your team wants to work. Common options include VS Code Remote SSH, JetBrains Gateway, Cursor, Zed, and plain SSH. -This keeps the agent and project-private network inside Zerops while letting you use a desktop editor UI. Broader editor patterns live in [Local & Remote Development](/features/local-remote-development#native-ide-over-ssh). +This keeps the agent and private network inside Zerops while letting you use a desktop editor UI. Broader editor patterns live in [Local & Remote Development](/features/local-remote-development#native-ide-over-ssh). You can also install another agent CLI or additional MCP servers inside the `zcp` service. The bundled Claude Code flow is a convenience, not a closed product boundary. ## Advanced customization guardrails -Tools installed inside the `zcp` service may see workspace environment variables, project-private networking, and any runtime files mounted into the workspace. Use trusted and pinned tools, and keep model credentials, git delivery tokens, external API keys, and production credentials out of the workspace unless the current task explicitly needs them. +Tools installed inside the `zcp` service may see workspace environment variables, private networking, and any runtime files mounted into the workspace. Use trusted and pinned tools. Keep model credentials, git delivery tokens, external API keys, and production credentials out of the workspace unless the current task explicitly needs them. ## What belongs where @@ -90,16 +90,16 @@ Tools installed inside the `zcp` service may see workspace environment variables | -------------------- | --------------------------------------------------------------------------------------------------------------------------- | | Agent workspace | The `zcp@1` service: agent CLI, browser VS Code, shell tools, MCP servers, helper processes, dotfiles. | | App code deploys | Your app runtime services; not the `zcp` workspace service. | -| ZCP project token | Injected by Zerops into the `zcp` workspace. | +| `ZCP_API_KEY` | Injected by Zerops into the `zcp` workspace. | | Agent account | Your Claude subscription login or model API credential. Zerops wires the agent to ZCP, but the agent account remains yours. | | Git credentials | Configured inside the workspace when the agent should commit or push from remote setup. | -| Production promotion | A separate production project and release path. | +| Production promotion | A separate production project and release process. | ## Runtime file access Remote setup can mount runtime service filesystems into the `zcp` workspace after the first setup pass. Each mounted runtime appears as its own folder. Editing a mounted file changes the file inside that runtime service, with no upload step from your laptop. -Filesystem reach is narrower than network reach. The workspace can reach project services over the private network, but it only sees runtime files that are mounted into it. +Filesystem reach is narrower than network reach. The workspace can reach services over the private network, but it only sees runtime files mounted into it. ## Make customization persistent @@ -109,9 +109,9 @@ Use these patterns: - **Small additions:** put package installs, dotfiles, or bootstrap scripts into service init commands. - **Team-standard workspace:** build a derived image based on `zcp@1` with required tools already present. -- **Helper processes:** run project-local helpers next to the agent and editor workspace when your team needs private integrations. +- **Helper processes:** run private helpers next to the agent and editor workspace when your team needs internal integrations. -Keep app runtime build steps with the runtime services. The remote workspace carries the agent, tools, and project access; runtime services own app builds and deploys. +Keep app runtime build steps with the runtime services. The remote workspace carries the agent, tools, and ZCP access; runtime services own app builds and deploys. ## Next steps diff --git a/apps/docs/content/zcp/setup/local-agent-bridge.mdx b/apps/docs/content/zcp/setup/local-agent-bridge.mdx index e94662aa7..ba3967982 100644 --- a/apps/docs/content/zcp/setup/local-agent-bridge.mdx +++ b/apps/docs/content/zcp/setup/local-agent-bridge.mdx @@ -1,13 +1,13 @@ --- -title: 'Set up local ZCP' -description: 'Install the zcp binary, initialize a local project directory, and connect a local agent to one Zerops project.' +title: 'How to run ZCP on your machine' +description: 'Install the zcp binary, initialize a local app directory, add ZCP_API_KEY, and connect a local agent to Zerops.' --- -Local setup runs ZCP on your machine. +Local setup installs the `zcp` binary on your machine and runs it from the app directory where your local agent works. -Use it when the agent should work next to local files, local data, your desktop editor, your terminal tools, and your local git credentials. ZCP still scopes Zerops operations to one project, but the agent client itself runs as your local user. Your local agent permissions and filesystem allowlists matter. +Use it when the agent should work next to local files, local data, your desktop editor, terminal tools, and git credentials. ZCP limits Zerops operations to one project, but the agent client itself runs as your local user. Your local agent approvals and filesystem allowlists matter. -[Remote setup](/zcp/setup/hosted-workspace) is the safer default when you do not need local files or tools. Use local setup when the local workflow is the point. +[Remote setup](/zcp/setup/hosted-workspace) is the safer default when the agent does not need local files or tools. Use local setup when local control is the point. :::warning Public preview Local setup has more moving parts than remote setup and may change faster. The binary install path, `.mcp.json` shape, and files written by `zcp init` are still settling between releases. @@ -18,34 +18,34 @@ Local setup has more moving parts than remote setup and may change faster. The b Pick the folder that should own app work: - **Empty local directory.** Start with no app code yet. The agent can create the app structure and use ZCP to select or create Zerops services. -- **Existing project directory.** Use this when app code, local data, editor setup, test fixtures, and git credentials already live on your machine. +- **Existing app directory.** Use this when app code, local data, editor setup, test fixtures, and git credentials already live on your machine. - **Recipe prepared for local setup.** Use a recipe to create the Zerops service baseline, then run the agent locally from the directory that should own source changes. -After that choice, the mechanics are the same: install `zcp`, run `zcp init`, add a project-scoped token, start VPN when private service access is needed, and link a deploy target when the agent should deploy. +After that choice, the mechanics are the same: install `zcp`, run `zcp init`, add `ZCP_API_KEY`, start VPN when private service access is needed, and link a deploy target when the agent should deploy. ## What local setup gives the agent - **Local files as source.** The agent edits the directory on your machine, and deploys use that working directory. - **Your editor and terminal.** Framework CLIs, test runners, local data, and local feedback stay under your normal tools. - **Your git credentials.** Pushes use your local git CLI, SSH agent, or credential helper. -- **Project-scoped Zerops operations.** ZCP lets the agent discover services, generate env snapshots, deploy to linked runtimes, read logs, and verify the project. -- **Private service access through VPN.** Your local app and shell reach project services through `zcli vpn up`. +- **Zerops operations.** ZCP lets the agent discover services, generate env snapshots, deploy to linked runtimes, read logs, and verify behavior. +- **Private service access through VPN.** Your local app and shell reach private service hostnames through `zcli vpn up`. Security note: local ZCP cannot protect your laptop from the agent client. Configure your local agent approvals the same way you would for any coding agent running on your machine. ## Prerequisites -- A Zerops project. Create one from the [Zerops dashboard](https://app.zerops.io/dashboard/project-add), from a [recipe](https://app.zerops.io/recipes), or use an existing development/staging project. +- A Zerops project. Create one from the [Zerops dashboard](https://app.zerops.io/dashboard/project-add), from a [recipe](https://app.zerops.io/recipes), or use an existing development/staging setup. - [zCLI](/references/cli) installed and authenticated on your machine. - A compatible local agent client installed and logged in. -- A **project-scoped Zerops token** for exactly one project. Multi-project tokens are refused at startup. +- A **single-project Zerops token**. Multi-project tokens are refused at startup. - A local directory where the agent should run. -You do not need ZCP just to develop locally against Zerops services as a human. `zcli vpn up` plus your editor is enough for that. Add ZCP when a local coding agent should also understand and operate the Zerops project. +You do not need ZCP just to develop locally against Zerops services as a human. `zcli vpn up` plus your editor is enough for that. Add ZCP when a local coding agent should also understand and operate Zerops. -## 1. Get a project-scoped token +## 1. Get `ZCP_API_KEY` -ZCP needs a Zerops API token scoped to exactly one project. For normal agent work, use a single-project token with full access to that project. Read-only tokens can authenticate but fail on deploys, env changes, lifecycle actions, and other mutations. +ZCP needs a Zerops API token that reaches exactly one project. For normal agent work, use a full-access token for that project. Read-only tokens can authenticate but fail on deploys, env changes, lifecycle actions, and other mutations. Token generation, rejected shapes, and rotation are covered in [Tokens and credentials](/zcp/security/tokens-and-project-access). Keep the token value available for the `.mcp.json` step below. @@ -71,15 +71,15 @@ From the local directory the agent should operate: zcp init ``` -`zcp init` writes the local MCP configuration and agent instructions for this project directory. +`zcp init` writes the local MCP configuration and agent instructions for this directory. Current generated artifacts: - `.mcp.json` - MCP configuration your local agent client reads to discover ZCP. -- `CLAUDE.md` - agent instructions for operating ZCP in this project. +- `CLAUDE.md` - agent instructions for operating ZCP here. - `.claude/settings.local.json` - Claude Code per-project settings when that client is used. -- `~/.config/zerops/aliases` plus a shell-rc sourcing line - helper aliases for launching the agent from this project. -- `.zcp/state/` - created later when ZCP first writes local project state. +- `~/.config/zerops/aliases` plus a shell-rc sourcing line - helper aliases for launching the agent from this directory. +- `.zcp/state/` - created later when ZCP first writes local state. Re-running `zcp init` may refresh generated config. `CLAUDE.md` preserves edits outside managed markers, but `.mcp.json` is regenerated from the token-less template. If you rerun it, re-check the `ZCP_API_KEY` env block before launching the agent again. @@ -94,32 +94,32 @@ Re-running `zcp init` may refresh generated config. `CLAUDE.md` preserves edits "command": "zcp", "args": ["serve"], "env": { - "ZCP_API_KEY": "" + "ZCP_API_KEY": "" } } } } ``` -Add `.mcp.json` to `.gitignore`. It contains a live project credential and should not leave the machine. Each project directory should have its own `.mcp.json` and its own project token. +Add `.mcp.json` to `.gitignore`. It contains a live Zerops credential and should not leave the machine. Each app directory should have its own `.mcp.json` and token. The server name `zerops` is intentional. Do not rename that key unless your agent client explicitly requires a different name and you understand the resulting prompt/instruction changes. -## 5. Launch the agent from the project root +## 5. Launch the agent from the app directory Start your local agent client from the same directory that contains `.mcp.json`. The client should list `zerops` as an available MCP server. Sanity check: ```text -Use ZCP to list the services in this project. +Use ZCP to list the Zerops services. ``` -A working connection answers with the project's runtime and managed services. If the agent cannot reach ZCP, check the launch directory, token scope, and whether the client loaded `.mcp.json`. +A working connection answers with the runtime and managed services. If the agent cannot reach ZCP, check the launch directory, token scope, and whether the client loaded `.mcp.json`. ## 6. Bring up VPN when private services are needed -ZCP can talk to the Zerops API without VPN. Your local app, shell, tests, database clients, and framework commands need VPN to reach project-private service hostnames such as `db` or `cache`. +ZCP can talk to the Zerops API without VPN. Your local app, shell, tests, database clients, and framework commands need VPN to reach private service hostnames such as `db` or `cache`. ```bash zcli vpn up @@ -127,25 +127,36 @@ zcli vpn up VPN setup needs admin or root approval on macOS and Linux. ZCP can tell the agent which command is needed, but it cannot approve or start the VPN for you. -After the tunnel is up, project service hostnames resolve from your machine. Laptop sleep, network changes, and idle time can drop the tunnel; run the command again when local service connections start failing. +After the tunnel is up, service hostnames resolve from your machine. Laptop sleep, network changes, and idle time can drop the tunnel; run the command again when local service connections start failing. ## 7. Generate local env when needed -When the local app needs project credentials, ask the agent for the app work and let ZCP generate the env bridge as part of that work. For a standalone check: +When the local app needs service credentials, ask the agent for the app work and let ZCP generate the env bridge as part of that work. For a standalone check: ```text Use ZCP to generate a .env file for my local app. ``` -ZCP resolves the env references your app would see when deployed and writes a `.env` snapshot in the working directory. The file is a snapshot, not a live sync. Regenerate after changing project env variables. +Env generation needs a real local deploy contract: -Keep `.env` out of git. It contains real project connection values. +- a `zerops.yaml` in the working directory, +- the runtime or setup the local app should use, +- a matching `setup:` entry in `zerops.yaml`, +- non-empty `run.envVariables` under that setup. + +ZCP reads `run.envVariables`, resolves Zerops references such as `${db_user}`, `${db_password}`, and `${db_hostname}`, and writes the resulting values into `.env`. Cross-service references resolve recursively, so `DATABASE_URL=postgres://${db_user}:${db_password}@${db_hostname}:${db_port}/${db_dbName}` lands as a complete local connection string. + +If those inputs are missing, the agent should fix the local `zerops.yaml` or ask for the target runtime/setup. It should not invent env values from service names or dashboard memory. + +The file is a snapshot, not a live sync. Regenerate it after changing env variables in Zerops. VPN is still required for your local app to use private service hostnames from that `.env`. + +Keep `.env` out of git. It contains real connection values. ## 8. Link a deploy target -Local setup needs a Zerops runtime when the agent should deploy from your working directory. A stage runtime is the usual target because it lets local work prove itself before any production promotion path. +Local setup needs a Zerops runtime when the agent should deploy from your working directory. A stage runtime is the usual target because it lets local work prove itself before production promotion. -If the project has exactly one runtime, ZCP can use it automatically. If multiple runtimes exist, the agent should ask which one to link. Answer by hostname, for example: +If there is exactly one runtime, ZCP can use it automatically. If multiple runtimes exist, the agent should ask which one to link. Answer by hostname, for example: ```text Link this local directory to appstage for deploys. @@ -163,15 +174,16 @@ ZCP does not own your git credentials. In local setup, your local git CLI, SSH a ## Local setup checks -- **Launch directory matters.** Start the agent from the folder that contains `.mcp.json`; otherwise it may connect to no ZCP server or the wrong project. +- **Launch directory matters.** Start the agent from the folder that contains `.mcp.json`; otherwise it may connect to no ZCP server or the wrong token. - **Multi-project tokens are rejected.** Use a token scoped to exactly one project. - **`zcp init` can remove the token from `.mcp.json`.** Re-add `ZCP_API_KEY` after rerunning init. - **VPN is separate from ZCP auth.** The agent may reach ZCP while your app still cannot reach `db` because VPN is down. -- **`.env` is a credential snapshot.** Regenerate after project env changes and keep it out of git. -- **Production stays out of this loop.** Local ZCP is for development and staging projects. +- **`.env` generation depends on `run.envVariables`.** If generation fails, check that the working directory has `zerops.yaml`, the selected `setup:` exists, and env entries live under `run.envVariables`. +- **`.env` is a credential snapshot.** Regenerate after env changes and keep it out of git. +- **Production stays out of this loop.** Local ZCP is for development and staging. ## Next steps -- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare setup paths. +- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare setups. - [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and destructive confirmations. 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 57c805d31..647b5323c 100644 --- a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx +++ b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx @@ -4,8 +4,8 @@ description: 'Development and verification are now covered in Build with ZCP.' unlisted: true --- -This page moved to [Build with ZCP](/zcp/workflows/build-with-zcp). +This content now lives in [Build with ZCP](/zcp/workflows/build-with-zcp). -Development, deploy, verification, and evidence-driven fixes are part of the normal ZCP development lifecycle. The user-facing development controls live in [Build with ZCP -> Develop with live project context](/zcp/workflows/build-with-zcp#develop-with-live-project-context). The deeper verification model lives in [How ZCP works -> Verification has two layers](/zcp/concept/how-it-works#verification-has-two-layers). +Use that guide for the user-facing development controls. Use [How ZCP works -> Verification has two layers](/zcp/concept/how-it-works#verification-has-two-layers) for the deeper verification model. For symptoms and recovery moves, use [Troubleshooting](/zcp/reference/troubleshooting). diff --git a/apps/docs/content/zcp/workflows/build-with-zcp.mdx b/apps/docs/content/zcp/workflows/build-with-zcp.mdx index 727579509..9a5123475 100644 --- a/apps/docs/content/zcp/workflows/build-with-zcp.mdx +++ b/apps/docs/content/zcp/workflows/build-with-zcp.mdx @@ -1,6 +1,6 @@ --- title: 'Build with ZCP' -description: 'The practical ZCP development surface: runtime layout, live project context, and delivery preference.' +description: 'How to give ZCP app work: runtime layout, live context, acceptance criteria, and delivery preference.' --- Build with ZCP starts from product intent, not from an operations checklist. @@ -10,9 +10,14 @@ Build a task board. Tasks should stay saved after refresh. ``` -Behind that prompt, ZCP gives the agent a development surface for one Zerops project: settle the runtime layout when it is unclear, work from live project context and Zerops-specific guidance, then close according to the delivery preference after proof. +The example is short so the ZCP work stays visible: service discovery, runtime fit, Zerops wiring, deploy, verification, evidence reading, recovery, and continuation from current state. -Prompt for the outcome, constraints, acceptance criteria, runtime layout, and delivery preference when they matter. ZCP treats deploy, verification, evidence reading, and proof or blocker reporting as part of completion. +Short does not mean one-shot or shallow. The same loop can carry detailed engineering work: stack choices, architecture constraints, multi-step acceptance criteria, existing-code inspection, delivery preferences, external credentials, and follow-up sessions over the same Zerops environment. + +:::note Prompt depth + +Start short when the outcome is enough. Add detail when it changes product behavior, architecture, stack, runtime layout, acceptance criteria, delivery, credentials, or safety approvals. ZCP handles the Zerops work behind both short and detailed prompts. +::: ## The lifecycle @@ -34,9 +39,9 @@ Prompt for the outcome, constraints, acceptance criteria, runtime layout, and de

2. Development context

-

Give code-facing work live project state.

+

Give code-facing work live state.

    -
  • Live project state
  • +
  • Live state
  • Relevant knowledge
  • Deploy + verify
@@ -53,11 +58,11 @@ Prompt for the outcome, constraints, acceptance criteria, runtime layout, and de -The important user-facing controls are the runtime layout you want, the product or code change you ask for, and the delivery preference that should apply after the work is proven. You do not run the flows by hand; ZCP uses them to give the agent the right context and done criteria. +The controls that matter are the runtime layout you want, the product or code change you ask for, and the delivery preference after proof. You do not run the lifecycle by hand; ZCP uses it to give the agent context and done criteria. ## 1. Settle the runtime layout {#choose-the-runtime-layout} -When the project does not yet have a clear app runtime and dependency set, ZCP prepares the project layout before app feature work starts. It reads current project state, uses existing services when they fit, creates missing app runtimes or managed services when needed, and stops when the agent knows where app code belongs. +When the app runtime and dependencies are unclear, ZCP prepares the layout before feature work starts. It reads current state, uses existing services when they fit, creates missing app runtimes or managed services when needed, and stops when the agent knows where app code belongs. The user-facing choice that most affects this first step is the runtime layout. You can let ZCP infer it from the project, or you can name the layout in the prompt when it matters. @@ -67,7 +72,7 @@ The user-facing choice that most affects this first step is the runtime layout. | **Dev + stage** | You want a development runtime plus a separate runtime for review or promotion rehearsal. | `Build a Node.js API with PostgreSQL on dev+stage.` | | **Stage / linked target** | You work from local files or a single target runtime and want ZCP to use that target. | `Use appstage as the deploy target for this local app.` | -If you do not specify a layout, the agent should infer from the existing project and ask only when the choice changes cost, runtime layout, stage scope, credentials, or a destructive action. +If you do not specify a layout, the agent should infer it from current state and ask only when the choice changes cost, runtime layout, stage target, credentials, or a destructive action. The layout is about app runtimes, not where ZCP itself runs. Remote setup can use any of these layouts from the `zcp@1` workspace. Local setup usually works from local files into a linked stage or app runtime. @@ -75,20 +80,20 @@ Managed services such as PostgreSQL, Valkey, queues, search, storage, or mail ar ## 2. Develop with live project context {#develop-with-live-project-context} -Any instruction that implies interaction with code starts or continues app development: build, inspect, change, fix, continue, deploy, or verify app work. ZCP wraps that work with the project context the agent needs, but it does not replace the normal coding conversation between you and the agent. +Any instruction that touches code starts or continues app development: build, inspect, change, fix, continue, deploy, or verify. ZCP adds the Zerops context the agent needs, but it does not replace the normal coding conversation between you and the agent. -Inside app development, the agent can still read files, make implementation choices, run tools, and follow your product instructions. ZCP matters around that work: it supplies current project state, relevant Zerops knowledge for the services in the project, scoped operations, deploy evidence, verification checks, and recovery rules. +Inside app development, the agent can still read files, make implementation choices, run tools, and follow your product instructions. ZCP matters around that work: it supplies current state, relevant Zerops knowledge, bounded operations, deploy evidence, verification checks, and recovery rules. -| What the agent gets from ZCP | Why it matters during development | -| ---------------------------- | ------------------------------------------------------------------------------------------------------- | -| Live project state | Services, runtime layout, env-var keys, Zerops references, events, deploys, logs, and saved work state. | -| Zerops knowledge | Runtime and managed-service wiring, `zerops.yaml`, env refs, ports, public access, and deploy behavior. | -| Project-scoped tools | Operations for service setup, env vars, deploys, logs, lifecycle, verification, and recovery. | -| Workflow instructions | When to use existing services, when to create missing ones, when to read evidence, and when to ask. | +| What the agent gets from ZCP | Why it matters during development | +| ---------------------------- | ---------------------------------------------------------------------------------------------------- | +| Live state | Services, runtime layout, env-var keys, Zerops references, events, deploys, logs, and saved work. | +| Zerops knowledge | Runtime and managed-service wiring, `zerops.yaml`, env refs, ports, public access, and deploy rules. | +| Bounded operations | Service setup, env vars, deploys, logs, lifecycle, verification, and recovery. | +| Done criteria | When to use existing services, create missing ones, read evidence, ask, or report a blocker. | -That means you usually do not paste an infrastructure inventory, env wiring plan, or log summary into the chat. A short product prompt can be enough because the agent can ask ZCP what exists now. +That means you usually do not paste an infrastructure inventory, env wiring plan, or log summary into the chat. A short product prompt can be enough because the agent can ask what exists now. -What you control here is the product request, stack preference, acceptance criteria, and any instruction to inspect or continue existing work. ZCP gives the agent the deploy, verification, evidence reading, and recovery loop behind that request. +What you control here is the product request, stack preference, acceptance criteria, and any instruction to inspect or continue existing work. ZCP gives the agent the deploy, verification, evidence reading, and recovery loop for that request. Expect the agent to stop for external credentials, destructive actions, cost-affecting infrastructure, ambiguous runtime or stage choices, and product decisions it cannot infer. @@ -97,12 +102,12 @@ Expect the agent to stop for external credentials, destructive actions, cost-aff | **Product behavior** | The feature or app outcome matters more than the exact stack. | `Build a task board where tasks stay saved after refresh.` | | **Stack preference** | You want a specific runtime, framework, or managed service. | `Use Laravel and the existing PostgreSQL service.` | | **Acceptance criteria** | A specific behavior must be true before the task is done. | `A user can create a task, refresh the page, and still see it.` | -| **Runtime layout** | The target layout matters for the work or review path. | `Build a Node.js API with PostgreSQL on dev+stage.` | -| **Existing work** | A previous ZCP session stopped or the project already exists. | `Read ZCP status first, then continue the interrupted task board work.` | +| **Runtime layout** | The target layout matters for the work or review target. | `Build a Node.js API with PostgreSQL on dev+stage.` | +| **Existing work** | A previous ZCP session stopped or the app already exists. | `Read ZCP status first, then continue the interrupted task board work.` | ## 3. Choose delivery preference {#choose-delivery-after-proof} -Delivery preference is how app work closes after there is a verified result. You can include it in the original prompt or set it later. +Delivery preference is how app work closes after there is a verified result. Include it in the original prompt or set it later. The first functional deploy is still direct so the agent can prove the app actually runs. Delivery preference decides what happens after that proof and how later ZCP sessions should finish similar work. @@ -112,9 +117,9 @@ The first functional deploy is still direct so the agent can prove the app actua | **Push to git** | The agent commits and pushes working changes to the configured repository. | `When the app works, push changes to git@github.com:my-org/task-board.git.` | | **CI / handoff** | A repository integration, GitHub Actions workflow, release process, or human owns the next deploy. | `Set up GitHub Actions delivery for future deploys after the app works.` | -ZCP records the delivery choice so later work can follow the same path without re-explaining the handoff every time. Git push credentials, CI secrets, and production credentials are separate from `ZCP_API_KEY`; see [Tokens and credentials](/zcp/security/tokens-and-project-access). +ZCP records the delivery choice so later work can follow it without re-explaining the handoff. Git credentials, CI secrets, and production credentials are separate from `ZCP_API_KEY`; see [Tokens and credentials](/zcp/security/tokens-and-project-access). -Production should still be a separate Zerops project. ZCP can prepare the handoff from dev or stage, but production promotion belongs to your release path; see [Production boundary](/zcp/security/production-policy). +Production should still be a separate Zerops project. ZCP can prepare the handoff from dev or stage, but production promotion belongs to your release process; see [Production boundary](/zcp/security/production-policy). ## What the final answer should contain @@ -128,7 +133,7 @@ For a completed app task, the agent should report: If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the decision or credential needed from you. -Advanced reuse path: [Package a running service](/zcp/workflows/package-running-service) turns a verified runtime into a re-importable bundle. It is not the normal lifecycle for the next app change. +Advanced reuse: [Package a running service](/zcp/workflows/package-running-service) turns a verified runtime into a re-importable bundle. It is not the normal lifecycle for the next app change. ## Next steps diff --git a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx index 9472fa725..d8685c028 100644 --- a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx +++ b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx @@ -4,8 +4,8 @@ description: 'Infrastructure setup is now covered in Build with ZCP.' unlisted: true --- -This page moved to [Build with ZCP](/zcp/workflows/build-with-zcp). +This content now lives in [Build with ZCP](/zcp/workflows/build-with-zcp). -Infrastructure setup is part of the normal ZCP development lifecycle. The current guide explains how ZCP helps the agent choose between dev, dev+stage, or a stage/linked target layout, use existing services, create missing runtimes or managed services, and move into app work once the project layout is clear. +Use that guide for runtime layout, existing services, missing services, and the handoff from setup into app work. For exact route names and workflow vocabulary, use [Workflow terms](/zcp/reference/agent-workflow). diff --git a/apps/docs/content/zcp/workflows/delivery-handoff.mdx b/apps/docs/content/zcp/workflows/delivery-handoff.mdx index 5b60a049f..069f63525 100644 --- a/apps/docs/content/zcp/workflows/delivery-handoff.mdx +++ b/apps/docs/content/zcp/workflows/delivery-handoff.mdx @@ -4,8 +4,8 @@ description: 'Delivery preference is now covered in Build with ZCP.' unlisted: true --- -This page moved to [Build with ZCP](/zcp/workflows/build-with-zcp). +This content now lives in [Build with ZCP](/zcp/workflows/build-with-zcp). -Delivery preference is part of the normal ZCP development lifecycle. The current guide explains the three user-facing choices: keep direct deploy, push verified work to git, or hand off to CI/release tooling. ZCP records the delivery choice so later work can follow the same path without re-explaining the handoff every time. +Use that guide for the three user-facing delivery choices: keep direct deploy, push verified work to git, or hand off to CI/release tooling. For exact internal labels, use [Workflow terms](/zcp/reference/agent-workflow#delivery-modes). diff --git a/apps/docs/content/zcp/workflows/package-running-service.mdx b/apps/docs/content/zcp/workflows/package-running-service.mdx index 2783424bb..309b5109f 100644 --- a/apps/docs/content/zcp/workflows/package-running-service.mdx +++ b/apps/docs/content/zcp/workflows/package-running-service.mdx @@ -3,7 +3,7 @@ title: 'Package a running service' description: 'Use ZCP to turn a running Zerops service into a re-importable bundle.' --- -Use packaging when a deployed runtime should become a reusable import bundle. This is an advanced handoff or reuse workflow, not the normal way to deploy the next app change. +Use packaging when a deployed runtime should become a reusable import bundle. This is an advanced handoff or reuse task, not the normal way to deploy the next app change. 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. The import file points back to the same source repo through `buildFromGit:`, so a fresh Zerops project can rebuild the app from git instead of carrying source code inside YAML. @@ -17,7 +17,7 @@ Packaging is the right tool when: Packaging is **not** the right tool when: -- You want the next app change deployed. For that, ask for the app result; direct deploy is the expected ZCP path behind the request. +- You want the next app change deployed. For that, ask for the app result; direct deploy is the expected ZCP behavior behind the request. - You want to deploy the next change to an existing production project. Keep production in its own Zerops project and operate it through Zerops UI, the API, or CI. Packaging does not keep a long-running session. If interrupted, the agent reads live state and prepares the bundle again. You only need to decide when the runtime choice or env-var classification is ambiguous. @@ -36,9 +36,9 @@ Use ZCP to package the appdev runtime so I can re-import this project into a fre The agent handles the packaging work; you only step in if it asks which runtime to package or how to classify a value. -## Classify env vars (secrets, project-scoped values, public values) +## Classify env vars -Once the runtime is chosen, ZCP prepares 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: +Once the runtime is chosen, ZCP prepares a candidate bundle and pauses on env vars. Each value needs a bucket so the generator knows what to do with it: | Bucket | What it means | What ends up in the bundle | | ----------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | @@ -117,7 +117,7 @@ A few things worth noticing: - 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). -Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen delivery preference expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through the normal [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). +Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen delivery preference expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through normal [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). ## What the bundle does not include @@ -130,7 +130,7 @@ Packaging can be part of a handoff intent. The agent should produce the files, p ## Packaging checks -1. **Packaging is not the delivery path by itself.** It produces the bundle. Commit and push happen through your chosen [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). +1. **Packaging is not delivery by itself.** It produces the bundle. Commit and push happen through your chosen [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). 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. diff --git a/apps/docs/sidebars.js b/apps/docs/sidebars.js index 9cd85397e..43d7fea3c 100644 --- a/apps/docs/sidebars.js +++ b/apps/docs/sidebars.js @@ -626,12 +626,12 @@ module.exports = { { type: 'doc', id: 'zcp/setup/hosted-workspace', - label: 'Use remote workspace', + label: 'What remote workspace gives you', }, { type: 'doc', id: 'zcp/setup/local-agent-bridge', - label: 'Set up local ZCP', + label: 'How to run ZCP on your machine', }, ], }, From 5424cd38437f2a2516554c32e8e5f0b3690c8467 Mon Sep 17 00:00:00 2001 From: krls2020 Date: Sun, 10 May 2026 23:14:23 +0200 Subject: [PATCH 14/24] Clarify ZCP docs framing --- apps/docs/content/zcp/concept/how-it-works.mdx | 4 +++- apps/docs/content/zcp/overview.mdx | 4 ++-- apps/docs/content/zcp/security/production-policy.mdx | 2 ++ apps/docs/content/zcp/setup/choose-workspace.mdx | 2 ++ apps/docs/content/zcp/setup/hosted-workspace.mdx | 4 +++- apps/docs/content/zcp/workflows/build-with-zcp.mdx | 2 ++ 6 files changed, 14 insertions(+), 4 deletions(-) diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx index 53bd514de..4bb14cca2 100644 --- a/apps/docs/content/zcp/concept/how-it-works.mdx +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -7,6 +7,8 @@ ZCP gives a coding agent an operating loop for Zerops app work. The agent reads The loop is carried by three things: bounded Zerops operations, Zerops-specific knowledge, and instructions for what to inspect, what to change, when to ask, and what counts as done. You do not run the loop by hand. You describe the product outcome; the agent uses the loop to make its decisions visible while it works. +The agent proves work against the same project pieces Zerops already uses: runtimes, managed services, env references, logs, events, and deploy results. A separate preview sandbox is not the source of truth. + ## The control loop ```mermaid @@ -159,7 +161,7 @@ Packaging a running service is a separate advanced reuse task. It turns a deploy ## Remote and local setup use the same loop -The same `zcp` binary can run in two places. The control loop stays the same; the filesystem and network access change. +The same `zcp` binary can run in two places. The control loop stays the same; the filesystem and network access change. A human can take over from the same evidence either way: files, runtime target, logs, events, verification result, and delivery state. | Setup | What runs where | Practical effect | | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index 0a77b5538..2ae4b7d1a 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -5,7 +5,7 @@ description: 'Why ZCP changes how a coding agent works with Zerops, and what you import Image from '/src/components/Image'; -ZCP is the project-scoped control plane for coding-agent work on Zerops. Zerops gives developers production-shaped infrastructure from the start: runtimes, managed services, private networking, env references, the deploy pipeline, logs, and public routes. ZCP gives the agent that same operating surface in a development or staging project, so its output can be verified on real infrastructure before production promotion. +Zerops provides the project infrastructure: runtimes, managed services, networking, env vars, deploys, logs, and public access. ZCP is the project-scoped MCP and workflow layer that lets a coding agent work with that infrastructure. A ZCP-backed task should end in one of two states: proof or a blocker. Proof means a deployed runtime plus the URL, endpoint response, UI state, worker result, or stored data that shows the requested behavior works. A blocker means the agent read the relevant Zerops evidence and names the missing credential, decision, unsupported fit, or repeated failure. @@ -35,7 +35,7 @@ Without ZCP, an app prompt often turns into an operations runbook. With ZCP, you ## Where ZCP runs -The **same `zcp` binary** runs in both setups. In [remote setup](/zcp/setup/hosted-workspace), Zerops packages it as a `zcp@1` service with Browser VS Code and bundled agent wiring. In [local setup](/zcp/setup/local-agent-bridge), you install the binary on your machine and connect your own editor or CLI agent. The model is the same: live state, bounded operations, deploy/verify evidence. The workspace, network access, deploy source, and safety profile differ. +The **same `zcp` binary** runs in both setups. In [remote setup](/zcp/setup/hosted-workspace), Zerops packages it as a `zcp@1` service with Browser VS Code and bundled agent wiring. In [local setup](/zcp/setup/local-agent-bridge), you install the binary on your machine and connect your own editor or CLI agent. The project surface is the same: live state, bounded operations, deploy/verify evidence. The workspace, network access, deploy source, and safety profile differ. To start using ZCP, choose a setup: add remote setup in Zerops, or initialize local setup beside your editor or CLI agent. The [Quickstart](/zcp/quickstart) uses remote setup because it needs no local install. diff --git a/apps/docs/content/zcp/security/production-policy.mdx b/apps/docs/content/zcp/security/production-policy.mdx index b71726d2b..dc3ca82f2 100644 --- a/apps/docs/content/zcp/security/production-policy.mdx +++ b/apps/docs/content/zcp/security/production-policy.mdx @@ -7,6 +7,8 @@ Use ZCP in development or staging. Keep production in a separate Zerops project Zerops does not prevent you from adding `zcp` to production. The policy exists because ZCP gives a coding agent operational access. In production, that is the wrong blast radius for normal app development. +The production boundary does not make ZCP output disposable. The intended path is production-shaped dev or stage infrastructure, verified behavior, and then a controlled production promotion outside the agent loop. + ## Recommended project layout | Project | Contains | Who operates it | diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index fe3b700eb..e5eb1133c 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -7,6 +7,8 @@ Remote setup is the default for most ZCP work. Use local setup when the agent ne Both setups use the same `zcp` binary and the same model: live state, bounded operations, and a finish line of proof or a blocker. The choice is where the agent workspace lives. +That means Browser VS Code, a desktop editor over SSH, a local CLI agent, or another MCP-capable client are workspace choices. Once connected to the project, the agent works from the same Zerops state and evidence. + :::tip Recommended default **Remote setup** gives you an agent workspace inside Zerops, no local install, private networking, and preconfigured Claude Code + Browser VS Code when enabled. ::: diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index d22a6d630..0eac1e3be 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -11,6 +11,8 @@ Use remote setup when you want the default workspace, a safer boundary for broad App code still deploys to your app runtime services. The `zcp` service is the workspace and control surface; it is not the application runtime. +Taking over is straightforward in this setup. You open the same workspace, terminal, files, and ZCP status the agent used, then continue, inspect, or stop the work from there. + ## What it includes - **`zcp@1` workspace service.** Runs ZCP inside Zerops and gives the agent bounded operations. @@ -66,7 +68,7 @@ For a returning remote workspace, open the existing `zcp` service: 3. Use **Browser VS Code** when **Cloud IDE** is enabled. 4. Complete the Claude Code login or API-token flow if prompted. -After authentication, Claude Code is connected to ZCP and ready to work from the Browser VS Code terminal. If the workspace was already authorized, use the same route to resume or supervise work. +After authentication, Claude Code is connected to ZCP and ready to work from the Browser VS Code terminal. If the workspace was already authorized, use the same route to resume, supervise, or take over work. First load can take a minute while the image, Cloud IDE, and bundled agent finish starting. If the page opens before the service is ready, wait until the service is running and reload. diff --git a/apps/docs/content/zcp/workflows/build-with-zcp.mdx b/apps/docs/content/zcp/workflows/build-with-zcp.mdx index 9a5123475..c58ee397e 100644 --- a/apps/docs/content/zcp/workflows/build-with-zcp.mdx +++ b/apps/docs/content/zcp/workflows/build-with-zcp.mdx @@ -14,6 +14,8 @@ The example is short so the ZCP work stays visible: service discovery, runtime f Short does not mean one-shot or shallow. The same loop can carry detailed engineering work: stack choices, architecture constraints, multi-step acceptance criteria, existing-code inspection, delivery preferences, external credentials, and follow-up sessions over the same Zerops environment. +The expected output is a verified running change, not only generated files. ZCP should prove the request against a real runtime, real managed services when the task uses them, and the logs, events, and checks that explain what happened. + :::note Prompt depth Start short when the outcome is enough. Add detail when it changes product behavior, architecture, stack, runtime layout, acceptance criteria, delivery, credentials, or safety approvals. ZCP handles the Zerops work behind both short and detailed prompts. From 31a5a125985881c0f3c11f26260ea0d42b436542 Mon Sep 17 00:00:00 2001 From: krls2020 Date: Mon, 11 May 2026 14:45:24 +0200 Subject: [PATCH 15/24] Refine ZCP MCP docs structure --- apps/docs/content/features/coding-agents.mdx | 169 ++++++++++-------- .../features/local-remote-development.mdx | 94 +++++----- .../docs/content/zcp/concept/how-it-works.mdx | 56 +++--- apps/docs/content/zcp/glossary.mdx | 48 ++--- apps/docs/content/zcp/overview.mdx | 58 +++--- apps/docs/content/zcp/quickstart.mdx | 10 +- .../content/zcp/reference/agent-workflow.mdx | 14 +- apps/docs/content/zcp/reference/index.mdx | 7 +- .../content/zcp/reference/mcp-operations.mdx | 42 ++--- .../content/zcp/reference/troubleshooting.mdx | 10 +- .../zcp/security/production-policy.mdx | 11 +- .../security/tokens-and-project-access.mdx | 8 +- .../docs/content/zcp/security/trust-model.mdx | 10 +- .../content/zcp/setup/choose-workspace.mdx | 12 +- .../content/zcp/setup/hosted-workspace.mdx | 12 +- .../content/zcp/setup/local-agent-bridge.mdx | 85 ++++----- .../zcp/workflows/build-and-verify-app.mdx | 8 +- .../content/zcp/workflows/build-with-zcp.mdx | 141 +++++++-------- .../workflows/create-or-adopt-services.mdx | 6 +- .../zcp/workflows/delivery-handoff.mdx | 6 +- .../zcp/workflows/package-running-service.mdx | 41 +++-- .../zcp/workflows/promote-to-production.mdx | 114 ++++++++++++ apps/docs/sidebars.js | 36 ++-- apps/docs/src/css/custom.css | 123 +++++++++++++ 24 files changed, 695 insertions(+), 426 deletions(-) create mode 100644 apps/docs/content/zcp/workflows/promote-to-production.mdx diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx index aca964f99..90eca8779 100644 --- a/apps/docs/content/features/coding-agents.mdx +++ b/apps/docs/content/features/coding-agents.mdx @@ -1,122 +1,143 @@ --- -title: ZCP for Coding Agents -description: ZCP gives coding agents current project knowledge and bounded operations on one Zerops project — discover, deploy, verify, recover, and hand off. +title: 'Infrastructure for Coding Agents' +description: 'Zerops gives coding agents the project infrastructure real app work needs: runtimes, managed services, private networking, deploys, logs, verification, and handoff.' --- -Coding agents need current project knowledge and a safe way to act on it. The hard part is rarely "write the function" - it's letting the agent connect product intent to infrastructure: services, env vars, logs, deploys, verification, and a path to production. +import Icons from '@theme/Icon'; -**Zerops Control Plane (ZCP)** gives a coding agent current project state and bounded operations on one Zerops project. You describe the app or change you want; ZCP lets the agent discover services, use existing infrastructure or create what is missing, wire the app to managed services, deploy through the standard pipeline, verify the real endpoint, inspect logs, and report a URL or blocker. You hold the product intent, constraints, approvals, and quality bar. +Coding agents can write code anywhere. Real application work needs more than an editor or a sandbox: the agent needs a project to operate, services to connect to, deploys to run, logs to inspect, and evidence that the requested behavior works. -The useful split is simple: **ZCP holds project reality; you hold intent.** The agent should not need you to paste a service map, env wiring plan, deploy log, or recovery checklist before it can do useful app work. +Zerops gives the agent that infrastructure surface. A Zerops project contains the runtimes, managed services, private networking, env vars, deployment pipeline, logs, and public access that the app actually uses. The agent can work against the same kind of infrastructure a human developer or staging release would use, not a pasted description of it. -:::caution Keep production in a separate project -ZCP runs with a project-scoped token that grants operational rights inside that project. Put it in a development or staging project, not the project serving production traffic. That keeps agent-driven changes away from production unless your CI or release process promotes them. See [Production boundary](/zcp/security/production-policy). -::: +**ZCP MCP** is the MCP tool layer that lets an agent read and operate one Zerops project through bounded project-scoped tools. The useful split is simple: **you hold product intent and judgment, Zerops holds project reality, and the MCP layer lets the agent work with that reality.** -## Who it's for +
+
+ You + Product intent +

Outcome, constraints, acceptance criteria, credentials, approvals.

+
+
+ Coding agent + App work +

Code changes, config wiring, deploy decisions, visible reasoning.

+
+
+ ZCP MCP + Project-scoped tools +

Live state, operations, evidence, optional workflow instructions.

+
+
+ Zerops project + Project reality +

Runtimes, services, private network, env refs, deploys, logs, URLs.

+
+
+ Result + Proof or blocker +

URL, endpoint result, UI state, worker result, stored data, evidence.

+
+
-Developers using a coding agent on application work that needs real infrastructure. The developer stays focused on the product outcome, technology choices, acceptance criteria, and judgment. ZCP gives the agent the platform context and operations needed to use or create services, connect the app to infrastructure, deploy, verify, recover from failure, and report back. +## For agent work that has to run -The **Include Coding Agent** option currently bundles Claude Code and wires it to ZCP. The agent account remains yours: authenticate it with your own subscription login or API credentials. Additional bundled agents are planned. Local setup can connect a compatible agent client running on your machine. +Use this when a coding agent is expected to produce a running Zerops app, not just files, a patch, or a preview artifact. -## What ZCP lets the agent do +You still decide the product outcome, stack constraints, acceptance criteria, repository policy, external credentials, and risky approvals. The agent gets the Zerops state and operations needed to use or create services, wire the app to managed infrastructure, deploy, verify, recover from failures, and report proof or a blocker. -ZCP exposes a fixed set of operations on one Zerops project, grouped by user job. The prompt can stay focused on the app because the agent does not need you to describe what the project looks like - ZCP reports it from what is deployed and running right now. +The agent can run in a Zerops-hosted workspace or from your local editor or CLI. The workspace is not the point; real infrastructure is. -| 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 with ZCP](/zcp/workflows/build-with-zcp#develop-with-live-project-context) | -| **Verify** | Reachability + behavior checks against the actual URL | [How ZCP works](/zcp/concept/how-it-works#verification-has-two-layers) | -| **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) | -| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | +## What infrastructure gives the agent -Behind one app request, the agent should read the project, use existing services or create missing ones, edit and deploy the app, verify the requested behavior, and then record how future changes should ship. A green deploy with a 500 on the relevant route is not done — verification has two layers and both must pass. +Without project infrastructure, an app prompt turns into an operations runbook. With Zerops infrastructure and ZCP MCP, the prompt can stay closer to the product because the agent can read and operate the project directly. -Full workflow terms: [Workflow terms](/zcp/reference/agent-workflow). +| Capability | What the agent gets | Why it matters | +| ---------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| **Live project state** | Runtime services, managed services, env-var keys, recent events, and deploy status. | The agent does not need a pasted service map or stale chat memory. | +| **Real dependencies** | Managed Postgres, Valkey, NATS, object storage, search, mail, and other services. | Product requirements can become real infrastructure, not local mocks. | +| **Private networking** | Services reachable by project hostnames and injected credentials. | App wiring follows the platform model the runtime actually uses. | +| **Deploy pipeline** | Builds and deploys through Zerops instead of a preview-only sandbox. | The first working result is already running on the platform. | +| **Logs and events** | Build logs, runtime logs, service status, and platform events. | Failure becomes an evidence surface instead of guesswork. | +| **Verification target**| Real URLs, endpoints, workers, databases, and stored objects. | Done means behavior proof, not just "the agent wrote code." | +| **Delivery path** | Direct deploy, git handoff, CI setup, packaging, or production promotion. | Verified work can move into the team's normal release process. | -## Two ways to run it +A healthy run ends in one of two states: proof or a blocker. Proof is an inspectable URL, endpoint response, UI state, worker result, or stored data that shows the requested behavior works. A blocker names the missing credential, decision, unsupported fit, or repeated failure with the relevant evidence. -The `zcp` binary can run in a Zerops service or on your laptop. The user choice is remote setup or local setup. +## What the MCP layer adds -**Remote setup** runs ZCP inside a Zerops `zcp@1` service. Enable **Include Coding Agent** for the bundled agent CLI, and **Cloud IDE** for Browser VS Code. This is the recommended starting point when broad agent permissions can stay inside the project. [What remote workspace gives you](/zcp/setup/hosted-workspace) +Zerops is the infrastructure: runtimes, managed services, private networking, env vars, deploys, logs, and public access. ZCP MCP is the tool surface for one selected Zerops project. -**Local setup** runs ZCP on your laptop. The agent runs in your local editor or CLI; private service access uses Zerops VPN. Use it when local files, tools, data, or a local-only agent client should stay in charge. [How to run ZCP on your machine](/zcp/setup/local-agent-bridge) +The tools give the agent operations, not judgment: discover services, manage configuration, deploy, inspect logs and events, recover from failures, and verify behavior against real services. Running `zcp` remotely or locally changes the workspace, filesystem, and network boundary, not the project model. -Either way, the agent gets the same project-scoped ZCP operations against the same project. Remote setup can package ZCP with an agent CLI and browser editor; local setup connects your own agent client to ZCP. +Generated workflow instructions are a separate layer. They teach sequencing: inspect state before changing things, choose the runtime target, use existing services or create missing ones, wire app code and Zerops config together, deploy, read failure evidence, verify requested behavior, and stop with proof or a blocker. -ZCP is also MCP-native. Advanced integrations can call the same project operations directly without using the Claude Code workflow layer; in that case your client owns the policy and sequencing. See [Advanced operations](/zcp/reference/mcp-operations#zcp-as-direct-mcp-tools). +You can also use ZCP MCP without the workflow instructions for custom MCP clients, scripts, dashboards, policy-gated clients, or narrow operational tasks. Then your client owns sequencing, verification, and when to ask a human. -Decision: [Remote or local ZCP setup](/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). +For setup and tool details: -## What remote setup adds +- [MCP overview](/zcp/overview) +- [Remote or local setup](/zcp/setup/choose-workspace) +- [MCP tools](/zcp/reference/mcp-operations) -Remote setup starts with a `zcp@1` Zerops service. **Include Coding Agent** adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use ZCP. **Cloud IDE** adds Browser VS Code so you can supervise or take over from the dashboard. +## Remote or local -Without **Include Coding Agent**, there is no preconfigured agent waiting inside the service. Without **Cloud IDE**, there is no browser editor. Configure both when you provision the service if you want the first-run agent + IDE path. Details: [What remote workspace gives you](/zcp/setup/hosted-workspace#what-it-includes). +Remote setup runs `zcp` inside a Zerops `zcp@1` service and can include the bundled coding agent and Browser VS Code. Local setup runs `zcp` on your machine so app files and git credentials stay local, with private service access through Zerops VPN. -Remote setup is still your workspace. You can install another agent CLI, add private MCP servers or helper processes, keep team tooling in the service, or use ZCP from another MCP-capable client. ZCP supplies the project-scoped control plane, operations, and workflow evidence; it does not lock the service to one agent or one set of tools. +Both paths expose the same project-scoped Zerops operations. Choose by where the agent and source tree should live, not by which infrastructure the app gets. For the broader human development model, see [Local & Remote Development](/features/local-remote-development). -## Why transparent infrastructure works for agents +## Why Zerops is legible to agents -Agents reason over text. The choices Zerops makes for human transparency are also what make the platform legible to agents: +Agents reason over text and tool results. Zerops is useful for agents because the platform is explicit in the same places it is useful for humans: -- `zerops.yaml` is structured and documented. The agent reads, modifies, and commits it the way it would any other config file. -- Resource allocation and scaling limits are queryable. When a change affects cost, the agent can surface the proposed resource change and ask 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. +- `zerops.yaml` is structured application and deploy configuration. +- Services have names, ports, env vars, and dependencies the agent can inspect. +- Managed-service references use platform-visible hostnames and credentials. +- Logs, deploy events, and service status are queryable. +- Runtime files can be reached through SSH or workspace mounts when that setup is used. +- Development, staging, and production can use the same Zerops-managed service types and platform model. -ZCP is the ergonomic layer on top of Zerops APIs, `zcli`, and SSH for agents. It tightens the loop by packaging project state, guidance, operations, and verification behind one project-scoped interface. +The MCP layer packages those surfaces into agent-friendly operations, but the underlying value is the project infrastructure itself. -## Operations and permissions +## Boundaries and permissions -ZCP groups its operations so an agent client or team policy can gate them: +The boundary is the project. The MCP server uses a project-scoped Zerops token, so the agent can operate only the project selected for that setup. Account-wide, organization-wide, and cross-project actions stay outside that token. -- **Read-only operations** inspect state, logs, events, configuration, and verification output. [Full list](/zcp/reference/mcp-operations#read-only-operations) -- **Mutating operations** deploy, change env vars, manage lifecycle, scale, or change public access. They are governed by token permissions and your agent/team policy. [Full list](/zcp/reference/mcp-operations#mutating-operations) -- **Destructive operations** have extra ZCP gates: service deletion needs explicit same-conversation approval naming the service, and destructive replacement after failed deploy history requires evidence review first. [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) -- **Operational setup** covers infrastructure, mounts, imports, work sessions, and delivery configuration. [Full list](/zcp/reference/mcp-operations#operational-setup) +Mutating operations still need to fit your agent policy and team rules. Destructive actions have extra confirmation gates. Git providers, CI, payment services, and production release credentials remain separate. -That's the surface. The boundary is the project. The token authorizes one project, full stop — cross-project, account-wide, or organization actions stay outside reach. Integration tokens are still bounded by the permissions of the user who created them and by the project access selected for the token; see [Roles & Permissions](/features/rbac). Full blast-radius framing: [Trust model](/zcp/security/trust-model). +For production, use coding-agent access in development or staging projects. Production should receive promoted work through your release or CI process, not through an agent session with broad project operations. See [Production boundary](/zcp/security/production-policy). -Full operation list and permission semantics: [Advanced operations](/zcp/reference/mcp-operations). Token shape and confirmation gates: [Tokens and credentials](/zcp/security/tokens-and-project-access). +Security details live in reference: -## Customize remote setup +- [Trust model](/zcp/security/trust-model) +- [Tokens and credentials](/zcp/security/tokens-and-project-access) -Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product. Install trusted tools, edit agent instructions, configure additional MCP servers, or ship a hardened version with team-standard tools baked in. Patterns and guardrails: [Make customization persistent](/zcp/setup/hosted-workspace#make-customization-persistent). +## What this is not -Local setup isn't extensible the same way; the binary on your laptop is just ZCP - editor, shell, local files, local data, and tool feedback stay on your machine. +Infrastructure for coding agents is not a prompt-to-prototype generator, an isolated code execution sandbox, or a cloud IDE with autocomplete. Those can be useful, but they do not by themselves give an agent the operational surface of a real app project. -## Source control - -When the agent runs in remote setup, git access belongs inside the workspace: GitHub CLI login, a fine-grained git token stored on the `zcp` service, or SSH agent forwarding from a remote editor. In local setup, it uses your existing local git credentials. Detail: [Tokens and credentials -> Git and CI credentials](/zcp/security/tokens-and-project-access#git-and-ci-credentials). - -## What ZCP is not - -ZCP is not a prompt-to-prototype generator, an isolated code sandbox, or an editor-only cloud development environment. It is the control plane that gives a coding agent project-scoped infrastructure operations on Zerops: services, env vars, deploys, logs, verification, recovery, and delivery handoff. +The claim is narrower: when the output must become a verified app on Zerops, the agent needs project infrastructure plus a safe way to read and operate it. ## How it differs from adjacent tools -ZCP is closest to the tools that let an agent work on real software over time, but the center of gravity is different: the agent operates one real Zerops project rather than a preview sandbox or editor-only workspace. +Most adjacent products solve a real part of agent work. The gap is usually what happens after code exists: where the app runs, which services it uses, whether the environment matches the release target, and how failures are diagnosed. -| Category | What it optimizes for | Where ZCP differs | -| ------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -| Prompt-to-prototype tools | Fast artifact generation and visual preview. | ZCP targets a running app on real services, with deploy, logs, persistence, and verification. | -| Code execution sandboxes | Safe isolated execution. | ZCP gives the agent managed services, private networking, deployment, and project state that persist across sessions. | -| AI-enabled cloud IDEs | Editing code in a hosted development environment. | ZCP gives the agent platform operations too: create/use services, wire env vars, deploy, verify, recover, and hand off. | -| Primitive/API collections | Calling many specialized APIs. | ZCP keeps the app and dependencies in one Zerops project, reachable by service names and standard platform mechanics. | +| | Persistent workspace | Real services | Deploy pipeline | Env parity | Standard tooling | +| --------------------- | :------------------: | :-----------: | :-------------: | :--------: | :--------------: | +| One-shot generators | - | - | - | - | - | +| Replit | ✓ | ~ | ~ | - | - | +| E2B | - | - | - | - | ✓ | +| Codespaces / Gitpod | ✓ | - | - | - | ✓ | +| Cloudflare-style | - | ~ | ~ | - | - | +| **ZCP** | **✓** | **✓** | **✓** | **✓** | **✓** | -The claim is not that ZCP replaces those tools. It solves the missing operational layer when the output must become a verified app running on Zerops infrastructure. +`~` means the category can cover part of the need, but not as one project model for agent-driven app work. Zerops does not replace the coding agent, and the MCP layer does not replace the developer. The difference is where the agent works: against the project infrastructure that will run the app. ## Where to start diff --git a/apps/docs/content/features/local-remote-development.mdx b/apps/docs/content/features/local-remote-development.mdx index 7041b601d..56f2e6ce7 100644 --- a/apps/docs/content/features/local-remote-development.mdx +++ b/apps/docs/content/features/local-remote-development.mdx @@ -1,29 +1,23 @@ --- title: Local & Remote Development -description: Three ways to develop on Zerops — local with VPN, Cloud IDE, or your native IDE over SSH. Same project network, service types, and deploy pipeline; production stays separate. +description: Three ways to develop on Zerops — local with VPN, Cloud IDE, 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. Across local VPN, Cloud IDE, and SSH, you use the same project network, hostnames, service types, and deploy pipeline. +Zerops development is network-first. Keep your editor on your laptop, work fully inside Zerops, or use a native editor over SSH; in each case you join the same project-private network and use the same hostnames, credentials, managed services, and deploy pipeline. -This is also what narrows the gap between development and production. Development and staging can use the same Zerops-managed service types, networking model, and `zerops.yaml` patterns production uses. Production should still live in a separate project with separate credentials, access rules, and resource policies. - -The same substrate works for humans and coding agents. A local agent inherits your laptop's VPN reach; an agent inside remote setup inherits the project's private network; an editor over SSH inherits the service or workspace it connects to. ZCP adds project operations and workflow evidence on top of that access, but local and remote development do not require ZCP. - -:::note -Remote setup is a `zcp@1` service you can add to a development or staging project. It can provide a Linux dev container, optional Cloud IDE, and optional **Include Coding Agent** wiring. The paths below can use that service, connect directly to runtime services, or stay fully local over VPN; local development with `zcli vpn up` does not require ZCP at all. -::: +Development, staging, and production can use the same Zerops-managed service types, networking model, and `zerops.yaml` patterns. What changes is environment policy: credentials, resource allocation, access rules, data, and release authority. ## 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. +Every project ships with a private network. Services reach each other by hostname (`db:5432`, `api:3000`, `redis:6379`), with credentials injected as environment variables. -The development question is just: how do you, the developer, get onto that network? Three answers give you the same project-private network reach, with different filesystem, tooling, and credential boundaries. +The development question is how you get onto that network. Three paths give you the same project-private reach, with different filesystem, tooling, and credential boundaries. -- **Local + VPN.** Your laptop joins the private network via WireGuard. Editor, toolchain, and processes stay where they are. Remote setup isn't required. -- **Cloud IDE.** 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 the `zcp` service, or directly to a service container. +- **Local + VPN.** Your laptop joins the private network via WireGuard. Editor, toolchain, and processes stay where they are. A hosted workspace is not required. +- **Cloud IDE.** A Linux workspace 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` into a workspace or directly into a runtime service container. -Same `db:5432`, same `api:3000`, same project credentials in all three. Switch paths without changing anything about the development project. +Same `db:5432`, same `api:3000`, same project credentials. Switch paths without changing the development project. ## Local + VPN @@ -37,11 +31,9 @@ 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. +Your laptop can reach project services by hostname: Postgres, Valkey, S3-compatible storage, runtime containers, and any other service in the project. You can keep your normal dev loop and point it at `db:5432` instead of a local mock or Docker Compose dependency. -What you don't have to do: add remote setup, 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 service type as stage or production. - -This path is also where coding agents running on your laptop fit. Without ZCP, a local agent client inherits the VPN's access, so the agent can hit `db:5432` and SSH into containers the same way you can. To let the agent operate the project - discover, deploy, verify, read logs, and manage delivery handoff - add [ZCP on your machine](/zcp/setup/local-agent-bridge). +You do not need a hosted workspace, `zcli push`, or a new editor for this path. **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. @@ -49,41 +41,48 @@ This path is also where coding agents running on your laptop fit. Without ZCP, a See the [VPN reference guide](/references/networking/vpn). ::: -## Cloud IDE on remote setup +## Cloud IDE in Zerops **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 remote setup (a `zcp@1` service) to a development or staging project - see [What remote workspace gives you](/zcp/setup/hosted-workspace) - you get a project-contained workspace with `zcp`, private-network access, optional Browser VS Code, and optional bundled agent wiring. +A hosted workspace is a normal Zerops service in the project. It gives you a project-contained Linux environment with private-network access, optional Browser VS Code, `zcli`, git tooling, database CLIs, and shell utilities. + +The workspace can also mount runtime service files, so you can inspect or edit code that runs in another container without moving it to your laptop: -Remote setup can also mount runtime service files into the workspace, so you can inspect or edit code that runs in another container without moving it to your laptop. Details live in [Runtime file access](/zcp/setup/hosted-workspace#runtime-file-access). +```bash +ls /var/www/apidev +ls /var/www/frontenddev +vim /var/www/apidev/src/server.ts +``` + +The dev server in the runtime container hot-reloads, the database is the same `db:5432`, and staging deploys still go through the Zerops deploy pipeline. 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.** The remote workspace 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 the `zcp` service's secret environment variables; `git` reads it through a credential helper. +- **Token in env vars.** Save a fine-grained personal access token to the workspace service's secret environment variables; `git` reads it through a credential helper. -Either pattern keeps your token in the `zcp` service, not in your project's source. +Either pattern keeps your token in the workspace service, 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`. +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 can use it: VS Code Remote SSH, JetBrains Gateway, Cursor, Zed, or plain `vim`. ```bash -ssh apidev # any service in the project +ssh apidev # any runtime service in the project ssh frontenddev -ssh zcp # if you provisioned the `zcp` service ``` -In this path, remote setup 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. Remote setup becomes a convenience layer: a single workspace that can carry tools, runtime file mounts, Cloud IDE, and coding-agent wiring for the project. If you'd rather connect your editor directly to your runtime services and skip remote setup entirely, that works. +In this path, a hosted workspace is convenient but not required. You can connect straight to `apidev`, edit the source there, and run the dev server in the same container. A hosted workspace is only a convenience layer when you want shared tools, runtime file mounts, or Cloud IDE. -**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. +**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 workspace service uses your laptop's GitHub key over the forwarded agent socket — no token storage required. ```ssh-config # Local ~/.ssh/config @@ -101,40 +100,33 @@ See the [SSH reference guide](/references/networking/ssh). | | Local + VPN | Cloud IDE | Native IDE over SSH | | --------------------- | ------------------ | -------------------------- | --------------------------------- | | Editor runs | Local | Browser | Local | -| Toolchain | Local | Remote setup | Remote setup 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 | -| Remote setup 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 one path. The same project can support all three without reprovisioning when your source-of-truth handoff is clear. A teammate on Local + VPN and an agent inside remote setup are hitting the same `db:5432`. - -For the agent-driven path, the setup decision (remote `zcp@1` service vs. local `zcp` binary) is on its own page: [Remote or local ZCP setup](/zcp/setup/choose-workspace). - -## Same architecture, separate production +You're not locked into one path. The same project can support all three without reprovisioning. A teammate on Local + VPN and another in Cloud IDE are hitting the same `db:5432`. -The development environments above don't approximate production by inventing a different platform. A Zerops development/staging project can run dev and stage services with the same Zerops-managed service types, private networking, load-balancer model, and `zerops.yaml` patterns that production uses in its separate project. Dev, stage, and production differ in resource allocation, access rules, and credentials, not in architecture. +## Same platform model from development to production -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 and stage, you've already tested it against the kind of infrastructure it'll meet in production. The remaining differences are scale, credentials, access policy, and data. +The environments above do not approximate production by inventing a different platform. Development, staging, and production can run the same Zerops-managed service types, private networking model, load-balancer model, and `zerops.yaml` patterns. The remaining differences are scale, credentials, access policy, data, and release authority. -This applies whether the developer is human or an agent. ZCP is the agent-facing control plane on top of the same project substrate; see [ZCP for Coding Agents](/features/coding-agents) for that case. +That makes the "but it works on my machine" failure mode harder to reach. Your dev Postgres, queue, cache, or object storage can be the same managed service type the app meets later in stage or production. ## How this differs from cloud IDEs -If you've used GitHub Codespaces or Gitpod, Cloud IDE 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, Cloud IDE looks similar: a Linux container in the cloud, accessed from a browser or local IDE. Two structural differences matter. -**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. +**You don't have to be in the editor.** Codespaces and Gitpod are editor-first. Zerops is network-first. Cloud IDE is one way to reach the project's private network; `zcli vpn up` lets you skip the editor environment and consume managed services from your existing local setup. -**Dev, staging, and production use the same platform model.** 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 model: managed Postgres, private networking, and `zerops.yaml`. Production still lives in its own project with its own credentials and policies. +**Development, staging, and production use the same platform model.** On a CDE (cloud development environment), your dev environment is one platform and your deployment target is another. On Zerops, managed services, private networking, and `zerops.yaml` patterns work the same way. Environment policy still differs: credentials, resources, access rules, data, and release process. -Remote setup 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. +A hosted workspace is just a Zerops service. 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](/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 → [ZCP for Coding Agents](/features/coding-agents) -- What remote workspace gives you → [What remote workspace gives you](/zcp/setup/hosted-workspace) -- Remote or local ZCP setup → [Remote or local ZCP setup](/zcp/setup/choose-workspace) -- Term reference → [ZCP Glossary](/zcp/glossary) +- Connect your laptop to a Zerops project → [VPN reference](/references/networking/vpn) +- Use a native editor over SSH → [SSH reference](/references/networking/ssh) +- Build and deploy from the same project model → [Build & deploy pipeline](/features/pipeline) +- Put a coding agent on top of this infrastructure → [Infrastructure for Coding Agents](/features/coding-agents) diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx index 4bb14cca2..f27e2c881 100644 --- a/apps/docs/content/zcp/concept/how-it-works.mdx +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -1,13 +1,13 @@ --- -title: 'How ZCP works' -description: 'The ZCP operating loop: live state, runtime target, service setup, app work, verification, recovery, and delivery.' +title: 'How it works' +description: 'The operating loop for live state, runtime target, service setup, app work, verification, recovery, and delivery.' --- -ZCP gives a coding agent an operating loop for Zerops app work. The agent reads live state, chooses the app runtime and dependencies, changes the app, deploys through Zerops, verifies real behavior, and returns proof or a blocker. +The ZCP MCP setup gives a coding agent an operating loop for Zerops app work. The agent reads live state, chooses the app runtime and dependencies, changes the app, deploys through Zerops, verifies real behavior, and returns proof or a blocker. -The loop is carried by three things: bounded Zerops operations, Zerops-specific knowledge, and instructions for what to inspect, what to change, when to ask, and what counts as done. You do not run the loop by hand. You describe the product outcome; the agent uses the loop to make its decisions visible while it works. +The loop is carried by bounded Zerops operations, Zerops-specific knowledge, and instructions for what to inspect, what to change, when to ask, and what counts as done. You describe the product outcome; the agent uses the loop to make its decisions visible while it works. -The agent proves work against the same project pieces Zerops already uses: runtimes, managed services, env references, logs, events, and deploy results. A separate preview sandbox is not the source of truth. +The source of truth is the real project: runtimes, managed services, env references, logs, events, and deploy results. A separate preview sandbox is not the source of truth. ## The control loop @@ -52,11 +52,11 @@ flowchart TD class blocker stop; ``` -The loop keeps the agent working from evidence instead of a guessed checklist. It knows where to read current state, which Zerops rules apply to app wiring, which evidence to read after deploy, and when the result is proof versus a blocker. +The loop keeps the agent working from evidence instead of a guessed checklist: current state, Zerops wiring rules, deploy evidence, and the distinction between proof and blocker. ## What "live state" means -ZCP reads Zerops instead of asking you to paste a service inventory into the prompt. Useful state includes: +The MCP tools read Zerops instead of asking you to paste a service inventory into the prompt. Useful state includes: - runtime services and managed services, - whether the app has one runtime, a dev+stage pair, or local files linked to a Zerops runtime, @@ -70,14 +70,14 @@ This is why a short prompt can be enough. The agent can ask what exists, which r Chat history is not the source of truth. If the agent sounds confused, starts from an old assumption, or a session was interrupted, the recovery move is: ```text -Read ZCP status and tell me where this project stands before changing anything. +Read current project status and tell me where this project stands before changing anything. ``` -## What ZCP coordinates for the agent +## What the workflow coordinates -ZCP is opinionated about the concerns an agent must resolve during app work. You usually do not name them in the prompt. ZCP supplies the state, tools, guidance, and done criteria so the agent can work from evidence. +The generated workflow is opinionated about the concerns an agent must resolve during app work. You usually do not name them in the prompt. The tools and instructions supply the state, guidance, operations, and done criteria so the agent can work from evidence. -| Concern | What ZCP gives the agent | What that means for you | +| Concern | What the agent gets | What that means for you | | ---------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | Live state | Services, env-var keys, Zerops references, recent events, logs, verification output, and saved work state. | You do not have to paste a service inventory or reconstruct what happened. | | Runtime target | Rules for choosing the app runtime that should receive code changes. | The agent can identify where app work belongs before editing or deploying. | @@ -92,19 +92,19 @@ The exact labels for layouts, delivery modes, and setup routes live in the [Glos ## Service setup prepares the project layout -Before app code work starts, ZCP helps the agent make three decisions visible: +Before app code work starts, the workflow makes three decisions visible: - Which runtime service is the app target? - Which managed services are dependencies? - Does the existing project layout fit the request? -If the services already exist, the agent can use them. If a needed runtime or dependency is missing, ZCP can create it. If the choice affects cost, product scope, credentials, a destructive action, or which stage/runtime should be used, the agent should stop and ask. +If the services already exist, the agent can use them. If a needed runtime or dependency is missing, the tools can create it. If the choice affects cost, product scope, credentials, a destructive action, or which stage/runtime should be used, the agent should stop and ask. -Service setup is finished when the app runtime and dependencies are known. It is not the finished app; it is the layout that lets app work happen in the right place. +Service setup is finished when the app runtime and dependencies are known. It is the layout that lets app work happen in the right place. ## App work changes code and platform wiring together -After the runtime target is known, ZCP gives the agent the platform knowledge needed to make the application change. In practice this often spans both source code and Zerops wiring: +After the runtime target is known, the agent gets the platform knowledge needed to make the application change. In practice this often spans both source code and Zerops wiring: - app files, - `zerops.yaml` build and run setup, @@ -113,13 +113,13 @@ After the runtime target is known, ZCP gives the agent the platform knowledge ne - start commands, ports, and public HTTP support, - local `.env` generation when using local setup. -ZCP guidance matters because Zerops is its own platform. Service references, build/deploy behavior, public access, scaling, and env resolution do not follow Docker Compose or Kubernetes conventions. The agent gets Zerops-specific rules instead of relying on a generic cloud template. +That guidance matters because Zerops is its own platform. Service references, build/deploy behavior, public access, scaling, and env resolution do not follow Docker Compose or Kubernetes conventions. -The first functional deploy goes directly through ZCP. That deploy creates the running result the agent can verify. A repository push or CI handoff can follow, but it should not replace the first proof. +The first functional deploy goes directly through MCP so the agent has a running result to verify. A repository push or CI handoff can follow, but it should not replace the first proof. ## Recovery is evidence-driven -When something fails, ZCP gives the agent the evidence surface that matches the failure: +When something fails, the agent has the evidence surface that matches the failure: | Failure pattern | Useful evidence | | ------------------------ | ------------------------------------------------------------------------------------------------------------ | @@ -130,11 +130,11 @@ When something fails, ZCP gives the agent the evidence surface that matches the | Config was rejected | Field-level rejection, `zerops.yaml`, setup name, env reference, service settings. | | Credential failed | The named credential surface: Zerops token, git token, SSH, managed-service credential, or external API key. | -Retrying the same deploy without new evidence is not progress. The loop pushes the agent toward one of three outcomes: fix from a cause, ask for the missing decision, or report a blocker with the category, evidence read, and attempts made. +Retrying the same deploy without new evidence is not progress. The loop pushes toward one of three outcomes: fix from a cause, ask for the missing decision, or report a blocker with the category, evidence read, and attempts made. ## Verification has two layers -A successful deploy proves that Zerops accepted the build and started the runtime. Platform reachability checks prove the service is running and reachable. They still do not prove the product request. +A successful deploy proves that Zerops accepted the build and started the runtime. Reachability checks prove the service is running and reachable. They still do not prove the product request. For a task-board app, useful behavior proof might be: @@ -145,7 +145,7 @@ For a task-board app, useful behavior proof might be: For an API task, proof might be a JSON response from the requested endpoint and stored data behind it. For a worker task, proof might be a processed job and the resulting database or object-storage state. For a staging request, proof belongs on the stage runtime, not only on dev. -The final answer should make that proof inspectable: runtime name, URL or endpoint, behavior checked, and delivery choice if one was set. +The final answer should make proof inspectable: runtime name, URL or endpoint, behavior checked, and delivery choice if one was set. ## Delivery happens after proof @@ -157,20 +157,20 @@ Delivery choice controls how future changes ship after a verified runtime exists | Push to git | You want commits, review, repository history, or a repo-triggered build. | | External handoff | CI, release management, or a human owns the next deploy. | -Packaging a running service is a separate advanced reuse task. It turns a deployed runtime into a re-importable bundle for another Zerops project. It is useful for handoff or reuse, not for deploying the next app change; see [Package a running service](/zcp/workflows/package-running-service). +Packaging a running service turns a deployed runtime into a re-importable bundle for another Zerops project. It is useful for handoff or reuse after proof, not for deploying the next app change; see [Package a running service](/zcp/workflows/package-running-service). ## Remote and local setup use the same loop -The same `zcp` binary can run in two places. The control loop stays the same; the filesystem and network access change. A human can take over from the same evidence either way: files, runtime target, logs, events, verification result, and delivery state. +The same `zcp` binary can run in two places. The control loop stays the same; filesystem and network access change. A human can take over from the same evidence either way: files, runtime target, logs, events, verification result, and delivery state. | Setup | What runs where | Practical effect | | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Remote setup | A `zcp@1` service runs the `zcp` binary inside Zerops. **Include Coding Agent** adds the bundled agent CLI; **Cloud IDE** adds Browser VS Code. | Work happens inside the remote workspace. The agent can use private networking and runtime file mounts. | -| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your local editor or CLI agent talks to it. | App files, local data, deploy source, and git credentials stay on your machine. Managed services are reached over Zerops VPN, and `.env` generation bridges credentials into your local app. | +| Remote setup | A `zcp@1` service runs the `zcp` binary inside Zerops. **Include Coding Agent** adds the bundled agent CLI; **Cloud IDE** adds Browser VS Code. | Work happens inside the remote workspace, with private networking and runtime file mounts. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your local editor or CLI agent talks to it. | App files, deploy source, and git credentials stay local. Managed services are reached over Zerops VPN, and `.env` generation bridges credentials into your local app. | -Choose setup by where the agent and filesystem should live: [Remote or local ZCP setup](/zcp/setup/choose-workspace). +Choose setup by where the agent and filesystem should live: [Remote or local setup](/zcp/setup/choose-workspace). -## Signs of a healthy ZCP run +## Signs of a healthy run A well-shaped run should: @@ -186,6 +186,6 @@ That is the practical difference between "the agent wrote code" and "the app tas ## Where to go deeper -- [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. +- [Build and ship](/zcp/workflows/build-with-zcp) - normal app work after setup. - [Workflow terms](/zcp/reference/agent-workflow) - exact runtime layouts, delivery terms, failure categories, and completion evidence. - [Troubleshooting](/zcp/reference/troubleshooting) - practical recovery when a run gets stuck. diff --git a/apps/docs/content/zcp/glossary.mdx b/apps/docs/content/zcp/glossary.mdx index d5fe94770..b60d02e01 100644 --- a/apps/docs/content/zcp/glossary.mdx +++ b/apps/docs/content/zcp/glossary.mdx @@ -1,19 +1,19 @@ --- title: 'Glossary' -description: 'Short definitions for ZCP terms, exact workflow labels, setup names, delivery modes, and credential names.' +description: 'Short definitions for ZCP MCP terms, exact workflow labels, setup names, delivery modes, and credential names.' --- -Use the exact labels here when you are reading ZCP status, writing agent policy, debugging a session, or reviewing a handoff. In normal prompts, describe the outcome you want. +Use the exact labels here when you are reading workflow status, writing agent policy, debugging a session, or reviewing a handoff. In normal prompts, describe the outcome you want. ## Core names -**ZCP** - Zerops Control Plane for coding agents: the Zerops operations and workflow layer documented in this section. +**ZCP MCP** - Zerops Control Plane MCP: the MCP tool surface that exposes project-scoped Zerops operations to coding agents. -**ZCP operations** - the bounded Zerops operations exposed to a coding agent. In MCP clients, this usually appears as the `zerops` server. +**ZCP MCP tools** - the bounded Zerops operations exposed to a coding agent. In MCP clients, this usually appears as the `zerops` server. -**MCP server** - the Model Context Protocol server that exposes ZCP operations to an agent client. +**MCP server** - the Model Context Protocol server exposed by the `zcp` binary. -**Agent client** - the editor, CLI, or hosted agent runtime that connects to ZCP operations. +**Agent client** - the editor, CLI, or hosted agent runtime that connects to ZCP MCP. **`zcp` binary** - the executable. It runs inside remote setup or on your laptop in local setup. @@ -21,21 +21,21 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, **`zcp@1` service** - the Zerops service type behind remote setup. -**zCLI** - the Zerops command-line client for humans, scripts, VPN, and CI. It coexists with ZCP. +**zCLI** - the Zerops command-line client for humans, scripts, VPN, and CI. It coexists with ZCP MCP. **zsc** - the in-container Zerops Setup Control utility used from `zerops.yaml`. -## Where ZCP runs +## Where ZCP MCP runs -**Remote setup** - ZCP running inside a Zerops `zcp@1` service. +**Remote setup** - the `zcp` binary running inside a Zerops `zcp@1` service. -**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use ZCP operations. +**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use ZCP MCP tools. **Cloud IDE** - browser-based VS Code served by remote setup. **Browser VS Code** - the dashboard entry point into the Cloud IDE. -**AI Agent environment** - a recipe environment preset that creates app services plus remote setup with **Include Coding Agent** enabled. It is a recipe preset, not a separate ZCP setup choice. +**AI Agent environment** - a recipe environment preset that creates app services plus remote setup with **Include Coding Agent** enabled. It is a recipe preset, not a separate ZCP MCP setup choice. **Local setup** - the `zcp` binary running on your laptop after `zcp init`, while your editor or CLI agent talks to it. @@ -62,7 +62,7 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, **Runtime target** - the app runtime the change targets, such as `appdev`, `appstage`, `app`, or a linked local target. -**Direct deploy** - a ZCP deploy from the current source to the scoped runtime. The first verified running result uses direct deploy before delivery setup is applied. +**Direct deploy** - a deploy from the current source to the scoped runtime through ZCP MCP. The first verified running result uses direct deploy before delivery setup is applied. **Reachability verification** - checks that the runtime exists, is running, has no recent blocking errors, and can answer an HTTP probe when it is an HTTP service. @@ -72,7 +72,7 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, **Proof** - user-inspectable completion evidence, such as a URL, endpoint result, UI state, processed job, or stored result. -**ZCP status** - a live project read the agent can use after interruption or confusion. Ask for it before letting the agent change anything else. +**Workflow status** - a live project and session read the agent can use after interruption or confusion. Ask for it before letting the agent change anything else. **Blocker** - a clear stop state with evidence: failure category, runtime in scope, what was tried, and what decision or credential is needed. @@ -86,13 +86,15 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, - `git-push` - commit and push to a configured remote, then observe/verify any tracked build. - `manual` - external CI, release process, or a human owns future delivery. -**Git-push capability** - whether ZCP has enough remote URL and credential setup to push from remote setup. It can exist even when the current delivery mode is `auto`. +**Git-push capability** - whether remote setup has enough remote URL and credential setup to push from remote setup. It can exist even when the current delivery mode is `auto`. -**Build integration** - repository-triggered build/deploy path that ZCP may configure or observe, such as a Zerops dashboard webhook or GitHub Actions. It is separate from git-push capability and delivery mode. +**Build integration** - repository-triggered build/deploy path that ZCP MCP may configure or observe, such as a Zerops dashboard webhook or GitHub Actions. It is separate from git-push capability and delivery mode. -**External handoff** - delivery mode where ZCP records proof and state, but a human or external system owns future deploys. +**External handoff** - delivery mode where the workflow records proof and state, but a human or external system owns future deploys. -**Package a running service** - advanced reuse workflow that turns one deployed runtime and its managed dependencies into a re-importable bundle. It is not the normal way to deploy the next app change. +**Package a running service** - handoff or reuse workflow that turns one deployed runtime and its managed dependencies into a re-importable bundle. It is not the normal way to deploy the next app change. + +**Production promotion** - handoff from verified dev or stage work into a separate production Zerops project: production infrastructure setup, production deploy trigger setup, and repeated releases without putting ZCP in production. **Packaging env bucket** - exact labels used when classifying env vars during packaging: @@ -109,7 +111,7 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, **Managed service** - database, cache, queue, search, storage, mail, or similar dependency. It provides connection details; it is not an app deploy target. -**Runtime layout** - which app runtime services ZCP should use: +**Runtime layout** - which app runtime services the workflow should use: - `standard` - dev runtime plus explicit stage runtime. - `dev` - one mutable development runtime. @@ -137,20 +139,20 @@ Use the exact labels here when you are reading ZCP status, writing agent policy, - `credential` - Zerops, git, SSH, managed-service, or external API credential failed. - `other` - no known category matched. -**Recovery hint** - structured next move surfaced by ZCP when the failure has an actionable recovery path. +**Recovery hint** - structured next move surfaced by ZCP MCP when the failure has an actionable recovery path. **Confirmation gate** - an operation that pauses until the user explicitly confirms the named target or consequence. -**Destructive import override** - import action that would replace an existing service stack. ZCP refuses first, names the affected services, and requires a matching acknowledgment before proceeding. +**Destructive import override** - import action that would replace an existing service stack. ZCP MCP refuses first, names the affected services, and requires a matching acknowledgment before proceeding. ## Credentials -**Single-project token** - Zerops token that can access exactly one project. ZCP expects this shape. +**Single-project token** - Zerops token that can access exactly one project. ZCP MCP expects this shape. -**`ZCP_API_KEY`** - single-project Zerops token used by ZCP. +**`ZCP_API_KEY`** - single-project Zerops token used by ZCP MCP. **`GIT_TOKEN`** - git provider credential used by remote git-push delivery when needed. **`ZEROPS_TOKEN`** - Zerops token commonly used by GitHub Actions or external CI that runs `zcli`. -**External secret** - third-party credential such as Stripe, OpenAI, Mailgun, or GitHub API access. ZCP can wire placeholders and env vars, but the secret itself remains your responsibility. +**External secret** - third-party credential such as Stripe, OpenAI, Mailgun, or GitHub API access. The agent can wire placeholders and env vars, but the secret itself remains your responsibility. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index 2ae4b7d1a..a8873a9da 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -1,43 +1,49 @@ --- -title: 'Zerops Control Plane for coding agents' -description: 'Why ZCP changes how a coding agent works with Zerops, and what you can leave out of the prompt.' +title: 'ZCP MCP for Zerops projects' +description: 'How the ZCP MCP server lets a coding agent work with one real Zerops project.' --- import Image from '/src/components/Image'; -Zerops provides the project infrastructure: runtimes, managed services, networking, env vars, deploys, logs, and public access. ZCP is the project-scoped MCP and workflow layer that lets a coding agent work with that infrastructure. +Zerops provides the project infrastructure: runtimes, managed services, networking, env vars, deploys, logs, and public access. ZCP MCP is the project-scoped tool layer that lets a coding agent work with that infrastructure through MCP. -A ZCP-backed task should end in one of two states: proof or a blocker. Proof means a deployed runtime plus the URL, endpoint response, UI state, worker result, or stored data that shows the requested behavior works. A blocker means the agent read the relevant Zerops evidence and names the missing credential, decision, unsupported fit, or repeated failure. +For the broader feature concept — why coding agents need project infrastructure rather than a sandbox or generated artifact — start with [Infrastructure for Coding Agents](/features/coding-agents). + +With the generated workflow instructions enabled, an app task should end in proof or a blocker. Proof means a deployed runtime plus the URL, endpoint response, UI state, worker result, or stored data that shows the requested behavior works. A blocker means the agent read the relevant Zerops evidence and names the missing credential, decision, unsupported fit, or repeated failure. Your prompt can stay about the product. Name the stack, runtime layout, acceptance criteria, delivery path, external credentials, or risky approval only when those decisions matter. -## What ZCP gives the agent +:::info Public preview +ZCP MCP is a public preview and is currently under active development. If you hit a bug, confusing behavior, or a missing workflow, please report it on Discord; feedback from real projects is especially useful. +::: + +## What the agent gets -**Current state.** Services, app runtime layout, managed dependencies, env-var keys and references, logs, events, deploy history, verification state, and current work state. +**Current state.** Services, runtime layout, managed dependencies, env-var keys and references, logs, events, deploy history, verification state, and saved work state. **Zerops operations.** Tools for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. -**Workflow.** ZCP combines two work loops: infrastructure for using existing Zerops services or creating missing ones, and development for code, `zerops.yaml`, deploy, verification, and delivery choice. The goal is to keep Zerops work aligned with platform and development best practices while staying behind the product task, not becoming another checklist. +**Workflow.** The generated instructions combine service setup and app development: inspect state, choose the runtime target, use or create services, wire code and `zerops.yaml`, deploy, verify, and choose delivery. The Zerops work stays behind the product task instead of becoming another checklist. **Evidence-based completion.** A build or deploy is not the finish line. A completed app task should end with a working URL, endpoint result, UI proof, or a blocker backed by logs, events, and verification evidence. ## What you no longer have to script -Without ZCP, an app prompt often turns into an operations runbook. With ZCP, you should not need to paste: +Without this layer, an app prompt often turns into an operations runbook. With MCP tools and workflow instructions enabled, you should not need to paste: - the service map, runtime target, dev/stage state, or managed-service inventory, - database credentials, private hostnames, env-var references, or generated connection strings, - build logs, runtime logs, event timelines, or a guess about why the last deploy failed, -- a deploy/verify/recovery script for every task; ZCP treats proof or a blocker as part of completion, -- a recap after the chat loses context; the agent can read current ZCP status. +- a deploy/verify/recovery script for every task, +- a recap after the chat loses context; the agent can read current workflow status. **You still own the decisions that need human judgment:** product intent, technology constraints, acceptance criteria, external credentials, repository policy, and approval for destructive actions. -## Where ZCP runs +## Where it runs -The **same `zcp` binary** runs in both setups. In [remote setup](/zcp/setup/hosted-workspace), Zerops packages it as a `zcp@1` service with Browser VS Code and bundled agent wiring. In [local setup](/zcp/setup/local-agent-bridge), you install the binary on your machine and connect your own editor or CLI agent. The project surface is the same: live state, bounded operations, deploy/verify evidence. The workspace, network access, deploy source, and safety profile differ. +The **same `zcp` binary** runs in both setups. In [remote setup](/zcp/setup/hosted-workspace), Zerops packages it as a `zcp@1` service with Browser VS Code and bundled agent wiring. In [local setup](/zcp/setup/local-agent-bridge), you install it on your machine and connect your own editor or CLI agent. The project surface is the same; the workspace, network access, deploy source, and safety profile differ. -To start using ZCP, choose a setup: add remote setup in Zerops, or initialize local setup beside your editor or CLI agent. The [Quickstart](/zcp/quickstart) uses remote setup because it needs no local install. +To start, add remote setup in Zerops or initialize local setup beside your editor or CLI agent. The [Quickstart](/zcp/quickstart) uses remote setup because it needs no local install.
Remote setup with the zcp service in Zerops and Claude Code running in browser VS Code
## Your agent, credentials, and workspace -**Agent account.** Remote setup can include a bundled agent CLI, currently Claude Code, already configured to use ZCP. Zerops wires the agent to the control plane; it does not provide or own your model account. You authenticate the agent with your own subscription login or API credentials. +**Agent account.** Remote setup can include a bundled agent CLI, currently Claude Code, already configured for MCP. Zerops wires the agent to the tools; you still authenticate with your own subscription login or API credentials. -**Zerops token.** ZCP connects through `ZCP_API_KEY`, a Zerops token limited to one project. Remote setup gets it from the platform; local setup reads it from your `.mcp.json`. Token details live in [Tokens and credentials](/zcp/security/tokens-and-project-access). +**Zerops token.** The MCP server connects through `ZCP_API_KEY`, a Zerops token limited to one project. Remote setup gets it from the platform; local setup reads it from `.mcp.json`. Token details live in [Tokens and credentials](/zcp/security/tokens-and-project-access). -**Workspace freedom.** The `zcp@1` service is still a normal Zerops service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and adapt the workspace around the bundled setup. ZCP is not a closed agent product. Details live in [What remote workspace gives you](/zcp/setup/hosted-workspace#make-customization-persistent). +**Workspace freedom.** The `zcp@1` service is still a normal Zerops service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and adapt the workspace. Details live in [What remote workspace gives you](/zcp/setup/hosted-workspace#make-customization-persistent). :::caution Production boundary -Use ZCP for development or staging work. Production should stay in a separate Zerops project and receive promoted work through your CI or release process; see [Production boundary](/zcp/security/production-policy). +Use this setup for development or staging work. Production should stay in a separate Zerops project and receive promoted work through your CI or release process; see [Promote to production](/zcp/workflows/promote-to-production) for the practical flow and [Production boundary](/zcp/security/production-policy) for the policy. ::: ## Start here @@ -68,22 +74,22 @@ First-read pages: | If you want to | Read | | ----------------------------------------- | -------------------------------------------------------- | | Try the guided hands-on path | [Quickstart](/zcp/quickstart) | -| Understand the agent operating loop | [How ZCP works](/zcp/concept/how-it-works) | -| Compare remote and local setup | [Remote or local ZCP setup](/zcp/setup/choose-workspace) | -| Start building after setup | [Build with ZCP](/zcp/workflows/build-with-zcp) | +| Understand the agent operating loop | [How it works](/zcp/concept/how-it-works) | +| Compare remote and local setup | [Remote or local setup](/zcp/setup/choose-workspace) | +| Start building after setup | [Build and ship](/zcp/workflows/build-with-zcp) | Specific tasks and reference: | If you want to | Read | | ---------------------------------------- | --------------------------------------------------------------------------- | | Understand remote workspace | [What remote workspace gives you](/zcp/setup/hosted-workspace) | -| Use your local editor or CLI agent | [How to run ZCP on your machine](/zcp/setup/local-agent-bridge) | -| Decide how finished work ships | [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | +| Use your local editor or CLI agent | [Run locally](/zcp/setup/local-agent-bridge) | +| Decide how finished work ships | [Build and ship](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | | Understand token access and boundaries | [Trust model](/zcp/security/trust-model) | -| Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | +| Understand the feature concept | [Infrastructure for Coding Agents](/features/coding-agents) | | Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | | Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | -## What stays outside ZCP +## What stays outside -ZCP is not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, scaling, and service references remain canonical for platform behavior. +This section is not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, scaling, and service references remain canonical for platform behavior. diff --git a/apps/docs/content/zcp/quickstart.mdx b/apps/docs/content/zcp/quickstart.mdx index 4ddbbe431..fe064564a 100644 --- a/apps/docs/content/zcp/quickstart.mdx +++ b/apps/docs/content/zcp/quickstart.mdx @@ -68,7 +68,7 @@ After authentication, continue into **Browser VS Code**. The workspace opens wit /> -You are now inside the remote workspace. The agent can read live state, use ZCP operations, reach private services, and deploy app changes to the runtimes created by the recipe. +You are now inside the remote workspace. The agent can read live state, use the MCP tools, reach private services, and deploy app changes to the runtimes created by the recipe. ## 4. Ask for a product outcome @@ -103,10 +103,10 @@ If the agent cannot finish, useful output names the blocker: missing credential, ## Next steps -Read [How ZCP works](/zcp/concept/how-it-works) for the model behind what just happened: live state, runtime fit, app wiring, deploy evidence, behavior proof, and blockers. +Read [How it works](/zcp/concept/how-it-works) for the model behind what just happened: live state, runtime fit, app wiring, deploy evidence, behavior proof, and blockers. -Read [Remote or local ZCP setup](/zcp/setup/choose-workspace) when you are deciding where the agent workspace should live. Remote setup keeps the agent inside Zerops; local setup keeps it next to your files, editor, data, and tools. +Read [Remote or local setup](/zcp/setup/choose-workspace) when you are deciding where the agent workspace should live. Remote setup keeps the agent inside Zerops; local setup keeps it next to your files, editor, data, and tools. -Read [Build with ZCP](/zcp/workflows/build-with-zcp) before giving the agent larger tasks. It explains the practical choices you can control during normal work: runtime layout, development context, and delivery preference. +Read [Build and ship](/zcp/workflows/build-with-zcp) before giving the agent larger tasks. It explains the practical choices you control during normal work: runtime layout, app request, delivery, packaging, and production promotion. -Before promoting anything to production, read [Production boundary](/zcp/security/production-policy). ZCP belongs in development or staging; production should receive verified work through your release process. +Before promoting anything to production, read [Promote to production](/zcp/workflows/promote-to-production). ZCP belongs in development or staging; production should receive verified work through your release process. diff --git a/apps/docs/content/zcp/reference/agent-workflow.mdx b/apps/docs/content/zcp/reference/agent-workflow.mdx index 78b62ec75..287dd8d79 100644 --- a/apps/docs/content/zcp/reference/agent-workflow.mdx +++ b/apps/docs/content/zcp/reference/agent-workflow.mdx @@ -1,9 +1,9 @@ --- title: 'Workflow terms' -description: 'Precise ZCP terms for setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence.' +description: 'Precise ZCP MCP terms for setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence.' --- -Exact vocabulary for ZCP reference, audits, policies, and handoffs. Day-to-day prompts should describe outcomes. +Exact vocabulary for ZCP MCP reference, audits, policies, and handoffs. Day-to-day prompts should describe outcomes. ## Session layers @@ -11,7 +11,7 @@ Most workflow mistakes come from confusing these layers: | Layer | What it is | What changes here | | ---------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------- | -| ZCP setup | Where ZCP runs: remote `zcp@1` service or local `zcp` binary. | No app code should be deployed to the setup itself. | +| ZCP MCP setup | Where the `zcp` binary runs: remote `zcp@1` service or local machine. | No app code should be deployed to the setup itself. | | Target runtime service | The app runtime: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys. | | Managed services | Databases, caches, queues, search, storage, mail. | Schema/data operations and credentials, never app code deploys. | @@ -51,7 +51,7 @@ Service setup stops when app runtimes and managed dependencies are known. App co ## Develop flow -The develop loop is the main ZCP work cycle. It closes only when reachability and requested behavior both pass. +The develop loop is the main workflow cycle. It closes only when reachability and requested behavior both pass. ```mermaid flowchart TD @@ -103,7 +103,7 @@ Delivery mode applies after a verified deploy and describes how future changes s | ---------- | ------------------ | ------------------------------------------------------------------------------------------------------------------ | | `auto` | Keep direct deploy | ZCP keeps deploying future changes directly to the target runtime. | | `git-push` | Push to git | The agent commits and pushes to a configured remote; any resulting build still needs observation and verification. | -| `manual` | External handoff | CI, release process, or a human owns future delivery; ZCP records evidence but does not initiate the next deploy. | +| `manual` | External handoff | CI, release process, or a human owns future delivery; the workflow records evidence but does not initiate the next deploy. | Git-push capability, delivery mode, and build integration are separate. A project can have git-push configured while still using direct deploy for a given session. @@ -152,10 +152,10 @@ A session is well-shaped if platform evidence and the agent report answer: | Question | Evidence | | ----------------------------------------------- | ------------------------------------------------------------------------- | -| Which setup route and runtime layout were used? | ZCP status, service metadata, service list. | +| Which setup route and runtime layout were used? | Workflow status, service metadata, service list. | | Where did service setup end and app work begin? | Work-session state, deploy history, file changes. | | Which runtime was deployed and verified? | Service events, deploy result, verify output. | | What behavior proved success? | Endpoint/UI/job/data proof in the final report. | | What controls future delivery? | Delivery mode, git-push state, build integration, or manual handoff note. | -Operation-name reference lives in [Advanced operations](/zcp/reference/mcp-operations). Recovery shortcuts live in [Troubleshooting](/zcp/reference/troubleshooting). +Operation-name reference lives in [ZCP MCP tools](/zcp/reference/mcp-operations). Recovery shortcuts live in [Troubleshooting](/zcp/reference/troubleshooting). diff --git a/apps/docs/content/zcp/reference/index.mdx b/apps/docs/content/zcp/reference/index.mdx index fa2e1da2c..01224767a 100644 --- a/apps/docs/content/zcp/reference/index.mdx +++ b/apps/docs/content/zcp/reference/index.mdx @@ -1,14 +1,13 @@ --- title: "Reference" -description: "Precise ZCP terms, operation names, troubleshooting, packaging, and glossary." +description: "Precise ZCP MCP terms, operation names, troubleshooting, and glossary." --- -Use reference pages when exact labels matter. Day-to-day app work starts in [Build with ZCP](/zcp/workflows/build-with-zcp). +Use reference pages when exact labels matter. Day-to-day app work starts in [Build and ship](/zcp/workflows/build-with-zcp). | Need | Page | |---|---| | Name setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence | [Workflow terms](/zcp/reference/agent-workflow) | -| Use ZCP as direct MCP tools, understand generated `.zcp`/Claude files, or look up operation names | [Advanced operations](/zcp/reference/mcp-operations) | -| Turn a running service into a re-importable bundle | [Package a running service](/zcp/workflows/package-running-service) | +| Use ZCP MCP tools directly, understand generated `.zcp`/Claude files, or look up operation names | [ZCP MCP tools](/zcp/reference/mcp-operations) | | Recover a stuck or confused session | [Troubleshooting](/zcp/reference/troubleshooting) | | Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | diff --git a/apps/docs/content/zcp/reference/mcp-operations.mdx b/apps/docs/content/zcp/reference/mcp-operations.mdx index d719b17a9..dcebda5c0 100644 --- a/apps/docs/content/zcp/reference/mcp-operations.mdx +++ b/apps/docs/content/zcp/reference/mcp-operations.mdx @@ -1,37 +1,37 @@ --- -title: 'Advanced operations' -description: 'Use ZCP as direct MCP tools, understand generated state, and look up common operation names.' +title: 'ZCP MCP tools' +description: 'Use ZCP MCP tools directly, understand generated state, and look up common operation names.' --- -Most users never need operation names. They matter when you configure agent-client policy, debug an MCP integration, or automate ZCP directly. For normal app work, ask the agent for outcomes. Recipe-authoring and ZCP-internal maintenance operations are intentionally out of scope. +Most users never need operation names. They matter when you configure agent-client policy, debug an MCP integration, or automate ZCP MCP directly. For normal app work, ask the agent for outcomes. Recipe-authoring and ZCP-internal maintenance operations are intentionally out of scope. -## ZCP as direct MCP tools +## Direct MCP calls -ZCP is an MCP server. Workflow instructions are not required for every integration. +ZCP MCP is the MCP server exposed by the `zcp` binary. Workflow instructions are not required for every integration. -Any MCP-capable client can connect to ZCP and call individual tools such as `zerops_discover`, `zerops_logs`, `zerops_events`, `zerops_deploy`, or `zerops_verify`. This is useful for custom agent clients, scripts, dashboards, policy-gated clients, or debugging sessions where you want one Zerops operation instead of a full ZCP-guided task. +Any MCP-capable client can connect to ZCP MCP and call individual tools such as `zerops_discover`, `zerops_logs`, `zerops_events`, `zerops_deploy`, or `zerops_verify`. This is useful for custom agent clients, scripts, dashboards, policy-gated clients, or debugging sessions where you want one Zerops operation instead of a full workflow-guided task. -The higher-level ZCP behavior comes from generated agent instructions, especially `CLAUDE.md`. Those instructions tell Claude Code when to call `zerops_workflow`, how to discover services, how to treat deploy and verify evidence, and when a task is done. The MCP server exposes the tools; the generated instructions teach the agent the lifecycle. Nothing requires another MCP client to call `zerops_workflow` unless you add equivalent policy yourself. +The higher-level behavior comes from generated agent instructions, especially `CLAUDE.md`. Those instructions tell Claude Code when to call `zerops_workflow`, how to discover services, how to treat deploy and verify evidence, and when a task is done. The MCP server exposes the tools; the generated instructions teach the agent the lifecycle. Nothing requires another MCP client to call `zerops_workflow` unless you add equivalent policy yourself. Using only the tools is valid. In that setup, your integration owns the sequencing: which services are in scope, when to deploy, what counts as verification, how to read logs or events, and when to stop. Tool-level gates still apply, including explicit service deletion approval and destructive import override acknowledgement. ## Generated files -`zcp init` and the remote ZCP workspace create a small amount of agent configuration around the MCP server. +`zcp init` and remote setup create a small amount of agent configuration around the MCP server. | File or directory | Created where | Purpose | | ----------------- | ------------- | ------- | -| `CLAUDE.md` | Remote workspace or local project directory | Claude Code instruction surface. ZCP writes a managed block between `` and ``. User content outside that block is preserved. | -| `.claude/settings.local.json` | Remote workspace or local project directory | Claude Code project settings and ZCP tool permissions for that directory. | +| `CLAUDE.md` | Remote workspace or local project directory | Claude Code instruction surface. ZCP MCP writes a managed block between `` and ``. User content outside that block is preserved. | +| `.claude/settings.local.json` | Remote workspace or local project directory | Claude Code project settings and ZCP MCP tool permissions for that directory. | | `.mcp.json` | Local project directory | Project-local MCP server config. It contains the local `zcp` command and the project-scoped `ZCP_API_KEY`; keep it out of git. | -| `~/.claude.json` and SSH config | Remote ZCP workspace | Workspace-level Claude Code and SSH wiring used inside the Zerops-hosted workspace. | -| `.zcp/state/` | Working directory where the MCP server runs | ZCP workflow state and service metadata for that project directory. | +| `~/.claude.json` and SSH config | Remote setup | Workspace-level Claude Code and SSH wiring used inside the Zerops-hosted workspace. | +| `.zcp/state/` | Working directory where the MCP server runs | Workflow state and service metadata for that project directory. | The managed `CLAUDE.md` block is refreshed by `zcp init` and by the MCP server when the block already exists. Put durable project instructions outside the ZCP markers. Edits inside the managed block are treated as generated content. ## `.zcp/state` -`.zcp/state/` is project-scoped state for workflow-aware ZCP use. It is not application source code and should not be committed. +`.zcp/state/` is project-scoped state for workflow-aware ZCP MCP use. It is not application source code and should not be committed. | Path | What it stores | | ---- | -------------- | @@ -41,17 +41,17 @@ The managed `CLAUDE.md` block is refreshed by `zcp init` and by the MCP server w | `.zcp/state/registry.json` | Session ownership and lookup data for active workflow state. | | `.zcp/state/locks/` | Local coordination locks for stateful operations. | -ZCP does not use `.zcp/state/` as a stale copy of the Zerops project. Service status, logs, events, runtime files, env-var values, and current platform configuration are read from Zerops or from the local filesystem when tools run. Local `.env` files are generated separately and may contain secrets; `.zcp/state/` is metadata for ZCP's own coordination. +ZCP MCP does not use `.zcp/state/` as a stale copy of the Zerops project. Service status, logs, events, runtime files, env-var values, and current platform configuration are read from Zerops or from the local filesystem when tools run. Local `.env` files are generated separately and may contain secrets; `.zcp/state/` is metadata for workflow coordination. -Do not edit `.zcp/state/` by hand during normal work. Use ZCP tools to reset, resume, iterate, or reconfigure workflow state. Deleting it intentionally discards ZCP's local memory of services and delivery setup for that directory; the tools can rediscover live Zerops state, but delivery preferences and workflow history may need to be configured again. +Do not edit `.zcp/state/` by hand during normal work. Use ZCP MCP tools to reset, resume, iterate, or reconfigure workflow state. Deleting it intentionally discards local memory of services and delivery setup for that directory; the tools can rediscover live Zerops state, but delivery preferences and workflow history may need to be configured again. ## Tool-only use -If you do not want ZCP to guide Claude Code through workflows, keep the MCP connection and remove the ZCP-managed workflow block from `CLAUDE.md`, or keep your own policy outside the ZCP markers. You can also use a different MCP client that never reads that file. +If you do not want the generated workflow to guide Claude Code, keep the MCP connection and remove the ZCP-managed workflow block from `CLAUDE.md`, or keep your own policy outside the ZCP markers. You can also use a different MCP client that never reads that file. -After that, direct MCP operations still work. The client can call `zerops_discover`, `zerops_logs`, `zerops_events`, `zerops_env`, `zerops_deploy`, `zerops_verify`, and the other tools directly. What you lose is the generated instruction layer that makes the agent plan around live state, use the workflow status, deploy with bounded retries, verify behavior, and report proof or a concrete blocker. +After that, direct MCP operations still work. The client can call `zerops_discover`, `zerops_logs`, `zerops_events`, `zerops_env`, `zerops_deploy`, `zerops_verify`, and the other tools directly. What you lose is the generated instruction layer that makes the agent plan around live state, use workflow status, deploy with bounded retries, verify behavior, and report proof or a concrete blocker. -This is a reasonable advanced setup when you are building your own integration or want narrow operational access. Be explicit about the policy you expect from the agent, and test the integration more carefully because ZCP is no longer steering the whole task lifecycle for you. +This is a reasonable advanced setup when you are building your own integration or want narrow operational access. Be explicit about the policy you expect from the agent, and test the integration more carefully because the generated workflow is no longer steering the whole task lifecycle for you. ## Permission classes @@ -69,7 +69,7 @@ This is a reasonable advanced setup when you are building your own integration o | `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | | `zerops_events` | Read service activity, deploys, builds, scaling, and failures. | | `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | -| `zerops_knowledge` | Fetch ZCP guidance or platform knowledge for the current state. | +| `zerops_knowledge` | Fetch ZCP MCP guidance or platform knowledge for the current state. | | `zerops_process` | Check a known async process; cancel is a mutating action. | ## Mutating operations @@ -87,7 +87,7 @@ This is a reasonable advanced setup when you are building your own integration o | Operation | Purpose | | ------------------- | ----------------------------------------------------------------------------------------------------- | -| `zerops_workflow` | Track, recover, or configure ZCP work sessions; also carries the package-running-service export flow. | +| `zerops_workflow` | Track, recover, or configure workflow sessions; also carries the package-running-service export flow. | | `zerops_import` | Import project/service definitions. Destructive override is gated. | | `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | | `zerops_preprocess` | Expand Zerops preprocessor expressions. | @@ -99,7 +99,7 @@ This is a reasonable advanced setup when you are building your own integration o | ------------- | ------------------------------------------------ | ------------------------------------------------------------- | | Files | Runtime files through SSHFS or remote containers | Local working directory | | Deploy source | Remote service or batch deploy inside Zerops | Local `workingDir` | -| Dev server | ZCP can run or inspect remote dev processes | Your local tool owns the dev server | +| Dev server | Remote setup can run or inspect remote dev processes | Your local tool owns the dev server | | Env bridge | Env vars are already available in Zerops | `.env` generation resolves Zerops references for local use | | Git | Workspace-managed credentials | User's local git credentials | diff --git a/apps/docs/content/zcp/reference/troubleshooting.mdx b/apps/docs/content/zcp/reference/troubleshooting.mdx index 50b0d23cf..97d22859a 100644 --- a/apps/docs/content/zcp/reference/troubleshooting.mdx +++ b/apps/docs/content/zcp/reference/troubleshooting.mdx @@ -6,10 +6,10 @@ description: 'Recover ZCP sessions from current state, failure category, logs, e Start recovery from current state, not from chat memory. ```text -Read ZCP status and tell me where things stand before changing anything. +Read current project status and tell me where things stand before changing anything. ``` -That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. ZCP can rebuild the picture from live services, recent deploys, logs, events, and saved work state. +That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. The agent can rebuild the picture from live services, recent deploys, logs, events, and saved work state. ## Start from the failure category @@ -90,7 +90,7 @@ These apply to local setup. Remote setup is already on the private network and d Use status when the chat-side picture drifts from reality: ```text -Read ZCP status, list the services in scope, and summarize the last deploy and verify result before changing anything. +Read current project status, list the services in scope, and summarize the last deploy and verify result before changing anything. ``` That is the right move when the agent says it has no context, a browser workspace was reopened, a CLI session restarted, or you are taking over from a previous run. @@ -114,7 +114,7 @@ Before destructive recovery, read service-scoped events, logs, deploy/verify res ## Related -- [Build with ZCP](/zcp/workflows/build-with-zcp) +- [Build and ship](/zcp/workflows/build-with-zcp) - [Workflow terms](/zcp/reference/agent-workflow) - [Tokens and credentials](/zcp/security/tokens-and-project-access) -- [How to run ZCP on your machine](/zcp/setup/local-agent-bridge) +- [Run locally](/zcp/setup/local-agent-bridge) diff --git a/apps/docs/content/zcp/security/production-policy.mdx b/apps/docs/content/zcp/security/production-policy.mdx index dc3ca82f2..0ad097390 100644 --- a/apps/docs/content/zcp/security/production-policy.mdx +++ b/apps/docs/content/zcp/security/production-policy.mdx @@ -3,9 +3,9 @@ title: 'Production boundary' description: 'Keep ZCP in development or staging. Promote verified work to production through CI, release tooling, or a deliberate human action.' --- -Use ZCP in development or staging. Keep production in a separate Zerops project without a `zcp` service. Production deploys should come from CI, a release pipeline, or a deliberate human `zcli` push using production credentials. +Use the MCP setup in development or staging. Keep production in a separate Zerops project without a `zcp` service. Production deploys should come from CI, a release pipeline, or a deliberate human `zcli` push using production credentials. -Zerops does not prevent you from adding `zcp` to production. The policy exists because ZCP gives a coding agent operational access. In production, that is the wrong blast radius for normal app development. +Zerops does not prevent you from adding `zcp` to production. The policy exists because ZCP MCP gives a coding agent operational access. In production, that is the wrong blast radius for normal app development. The production boundary does not make ZCP output disposable. The intended path is production-shaped dev or stage infrastructure, verified behavior, and then a controlled production promotion outside the agent loop. @@ -53,6 +53,8 @@ The agent can prepare the handoff by pushing code, summarizing verification evid Broader CI/CD reference: [CI/CD with Zerops](/guides/ci-cd). +For the practical workflow, see [Promote to production](/zcp/workflows/promote-to-production). + ## Credential rules | Credential | Production policy | @@ -90,6 +92,7 @@ ZCP's involvement stops at the handoff. Production execution belongs to the rele - [Trust model](/zcp/security/trust-model) - the boundary that makes this policy enforceable. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - token handling. -- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare development setups. -- [Build with ZCP](/zcp/workflows/build-with-zcp) - the dev/stage loop this policy assumes. +- [Remote or local setup](/zcp/setup/choose-workspace) - compare development setups. +- [Build and ship](/zcp/workflows/build-with-zcp) - the dev/stage loop this policy assumes. +- [Promote to production](/zcp/workflows/promote-to-production) - practical production promotion paths after ZCP proof. - [Backup](/features/backup) - production recovery outside ZCP. 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 81c3fd5a7..b69fac6c1 100644 --- a/apps/docs/content/zcp/security/tokens-and-project-access.mdx +++ b/apps/docs/content/zcp/security/tokens-and-project-access.mdx @@ -30,7 +30,7 @@ To generate the token: 2. Create a token and name it for the project, for example `zcp-`. 3. Choose **Custom access per project**. 4. Add exactly one project. -5. Set that project to **Full access** for normal ZCP work. +5. Set that project to **Full access** for normal ZCP MCP work. 6. Create the token and copy the value. Zerops shows it only at creation time. The token's blast radius equals the project and its granted permissions. Other projects, organization settings, and billing stay out of reach. Zerops [Roles & Permissions](/features/rbac#integration-tokens) remain the platform authority. @@ -55,7 +55,7 @@ Common messages: | `No authentication found: set ZCP_API_KEY or log in with zcli` | The `zcp` process did not receive a usable token. | | `AUTH_TOKEN_EXPIRED` or HTTP 401 | The token expired, was revoked, or is invalid. | -For ZCP setup, provide `ZCP_API_KEY`. `zcli` login is a diagnostic fallback, not the normal agent setup. +For ZCP MCP setup, provide `ZCP_API_KEY`. `zcli` login is a diagnostic fallback, not the normal agent setup. ## Where the token lives - remote vs local {#where-the-token-lives--remote-vs-local} @@ -123,7 +123,7 @@ Rotation is picked up on the next process start or CI run, not in the middle of ## What ZCP enforces for destructive actions -A valid token does not remove every guardrail. ZCP adds explicit confirmation for operations where the loss is not safely reversible from inside the conversation. +A valid token does not remove every guardrail. ZCP MCP adds explicit confirmation for operations where the loss is not safely reversible from inside the conversation. | Operation | Gate | | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | @@ -157,6 +157,6 @@ Threat model and boundaries: [Trust model](/zcp/security/trust-model). - [Trust model](/zcp/security/trust-model) - the access boundary this page enforces. - [What remote workspace gives you](/zcp/setup/hosted-workspace) - automatic token injection. -- [How to run ZCP on your machine](/zcp/setup/local-agent-bridge) - local `.mcp.json` token setup. +- [Run locally](/zcp/setup/local-agent-bridge) - local `.mcp.json` token setup. - [Production boundary](/zcp/security/production-policy) - why production gets separate credentials. - [GitHub integration](/references/github-integration) - CI secret usage with Zerops. diff --git a/apps/docs/content/zcp/security/trust-model.mdx b/apps/docs/content/zcp/security/trust-model.mdx index 85021341d..fc2e64739 100644 --- a/apps/docs/content/zcp/security/trust-model.mdx +++ b/apps/docs/content/zcp/security/trust-model.mdx @@ -1,6 +1,6 @@ --- title: 'Trust model' -description: 'How ZCP limits Zerops access, and how remote and local setup change the surrounding blast radius.' +description: 'How MCP access is limited, and how remote and local setup change the surrounding blast radius.' --- The trust model starts with one rule: one ZCP process operates one Zerops project. `ZCP_API_KEY` decides that boundary at startup, and ZCP refuses tokens that resolve to no project or multiple projects. @@ -72,16 +72,16 @@ This is why development and staging projects are the right place for ZCP. Produc ## Local setup specifics -- **The agent inherits local reach.** ZCP is limited to one project, but the local agent client can read files, run commands, and use credentials allowed by your client settings. +- **The agent inherits local reach.** ZCP MCP is limited to one project, but the local agent client can read files, run commands, and use credentials allowed by your client settings. - **Each local directory has its own config.** `.mcp.json` and `.zcp/state/` belong to one app directory. Launching from the wrong directory can connect the wrong token or no token. -- **VPN is outside ZCP authority.** Bringing up Zerops VPN needs your operating-system approval. ZCP cannot grant that for the agent. +- **VPN is outside MCP authority.** Bringing up Zerops VPN needs your operating-system approval. The tools cannot grant that for the agent. - **`.env` files are snapshots.** They contain real project credentials and should stay out of git. ## Human confirmation gates Most project operations do not get an extra ZCP-specific confirmation prompt. Deploys, env changes, restarts, scaling, and public-access changes are normal project operations and are audited through platform evidence. -ZCP adds hard gates where the loss is not safely reversible from the conversation: +ZCP MCP adds hard gates where the loss is not safely reversible from the conversation: | Operation | Gate | | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | @@ -115,5 +115,5 @@ Filter by service hostname when possible. Project-level timelines can include un ## Next steps - [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and confirmation gates. -- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare blast radius before setup. +- [Remote or local setup](/zcp/setup/choose-workspace) - compare blast radius before setup. - [Production boundary](/zcp/security/production-policy) - keep production outside the agent loop. diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index e5eb1133c..a8adffbd4 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -1,9 +1,9 @@ --- -title: 'Remote or local ZCP setup' -description: 'Where the ZCP workspace runs: inside Zerops or on your machine.' +title: 'Remote or local setup' +description: 'Where the zcp binary and agent workspace run: inside Zerops or on your machine.' --- -Remote setup is the default for most ZCP work. Use local setup when the agent needs your local files, data, tools, editor, or a local-only agent client. +Remote setup is the default for most agent work with Zerops. Use local setup when the agent needs your local files, data, tools, editor, or a local-only agent client. Both setups use the same `zcp` binary and the same model: live state, bounded operations, and a finish line of proof or a blocker. The choice is where the agent workspace lives. @@ -70,7 +70,7 @@ Remote setup and local setup are both public preview. Local setup has more movin Remote setup can start from: - **Recipe with AI Agent environment.** A guided path for a known stack. A recipe creates app services, managed services, and the `zcp@1` workspace together. -- **New Zerops setup with ZCP enabled.** Start from blank services or a custom stack and add ZCP during creation. +- **New Zerops setup with remote setup enabled.** Start from blank services or a custom stack and add the `zcp@1` service during creation. - **Existing development or staging setup.** Add the `zcp` workspace next to services that already exist. Do not add ZCP to production; promote verified work through your release process instead. Local setup can start from: @@ -90,7 +90,7 @@ The project can still use: - a single app runtime, - local files linked to a stage target. -That choice belongs to app work: [Build with ZCP](/zcp/workflows/build-with-zcp#choose-the-runtime-layout). +That choice belongs to app work: [Build and ship](/zcp/workflows/build-with-zcp#choose-the-runtime-layout). Stage is not production. Keep production in a separate Zerops project and promote work through your release process; see [Production boundary](/zcp/security/production-policy). @@ -113,5 +113,5 @@ Before switching, make the handoff explicit: ## Next steps - [What remote workspace gives you](/zcp/setup/hosted-workspace) -- [How to run ZCP on your machine](/zcp/setup/local-agent-bridge) +- [Run locally](/zcp/setup/local-agent-bridge) - [Trust model](/zcp/security/trust-model) diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index 0eac1e3be..86191c8ba 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -11,7 +11,7 @@ Use remote setup when you want the default workspace, a safer boundary for broad App code still deploys to your app runtime services. The `zcp` service is the workspace and control surface; it is not the application runtime. -Taking over is straightforward in this setup. You open the same workspace, terminal, files, and ZCP status the agent used, then continue, inspect, or stop the work from there. +Taking over is straightforward in this setup. You open the same workspace, terminal, files, and workflow status the agent used, then continue, inspect, or stop the work from there. ## What it includes @@ -22,7 +22,7 @@ Taking over is straightforward in this setup. You open the same workspace, termi - **Private networking.** The workspace can reach managed services by hostname without laptop VPN. - **Open workspace model.** You can add other agent CLIs, private MCP servers, helper processes, dotfiles, package installs, or a derived team image. -For the local alternative, see [How to run ZCP on your machine](/zcp/setup/local-agent-bridge). For the tradeoffs, see [Remote or local ZCP setup](/zcp/setup/choose-workspace). +For the local alternative, see [Run locally](/zcp/setup/local-agent-bridge). For the tradeoffs, see [Remote or local setup](/zcp/setup/choose-workspace). ## Choose a starting point @@ -30,7 +30,7 @@ For the local alternative, see [How to run ZCP on your machine](/zcp/setup/local Use the [Quickstart](/zcp/quickstart) when you want the guided recipe route. It covers the recipe catalog, **AI Agent** environment, Claude Code authentication, Browser VS Code, first product prompt, and proof. -After provisioning, continue with a product prompt in [Build with ZCP](/zcp/workflows/build-with-zcp). +After provisioning, continue with a product prompt in [Build and ship](/zcp/workflows/build-with-zcp). ### Recipe with AI Agent environment @@ -38,13 +38,13 @@ Use this when you want a guided stack baseline for development or staging. A rec Keep **Coding Agent** enabled when you want bundled Claude Code. Keep **Cloud IDE** enabled when you want Browser VS Code. -### New Zerops setup with ZCP enabled +### New Zerops setup with remote setup enabled Use this when you want blank services or a custom stack. 1. Open [Add new project](https://app.zerops.io/dashboard/project-add). 2. Enter the project name, region, and tags. -3. Enable **Add Zerops Control Plane (ZCP) service**. +3. Enable the `zcp@1` remote setup service. 4. Keep **Include Coding Agent** enabled if you want bundled Claude Code. 5. Keep **Cloud IDE** enabled if you want browser VS Code. 6. Create the project. @@ -117,6 +117,6 @@ Keep app runtime build steps with the runtime services. The remote workspace car ## Next steps -- [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. +- [Build and ship](/zcp/workflows/build-with-zcp) - normal app work after setup. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - how `ZCP_API_KEY`, git credentials, and CI secrets differ. - [Production boundary](/zcp/security/production-policy) - why production belongs in a separate project. diff --git a/apps/docs/content/zcp/setup/local-agent-bridge.mdx b/apps/docs/content/zcp/setup/local-agent-bridge.mdx index ba3967982..39a645ad8 100644 --- a/apps/docs/content/zcp/setup/local-agent-bridge.mdx +++ b/apps/docs/content/zcp/setup/local-agent-bridge.mdx @@ -1,11 +1,11 @@ --- -title: 'How to run ZCP on your machine' +title: 'Run locally' description: 'Install the zcp binary, initialize a local app directory, add ZCP_API_KEY, and connect a local agent to Zerops.' --- -Local setup installs the `zcp` binary on your machine and runs it from the app directory where your local agent works. +Local setup installs the `zcp` binary on your machine and runs it from the app directory where your agent works. -Use it when the agent should work next to local files, local data, your desktop editor, terminal tools, and git credentials. ZCP limits Zerops operations to one project, but the agent client itself runs as your local user. Your local agent approvals and filesystem allowlists matter. +Use it when the agent should work next to local files, local data, your desktop editor, terminal tools, and git credentials. The MCP server limits Zerops operations to one project, but the agent client runs as your local user, so local approvals and filesystem allowlists matter. [Remote setup](/zcp/setup/hosted-workspace) is the safer default when the agent does not need local files or tools. Use local setup when local control is the point. @@ -17,21 +17,21 @@ Local setup has more moving parts than remote setup and may change faster. The b Pick the folder that should own app work: -- **Empty local directory.** Start with no app code yet. The agent can create the app structure and use ZCP to select or create Zerops services. +- **Empty local directory.** Start with no app code yet. The agent can create the app structure and use the MCP tools to select or create Zerops services. - **Existing app directory.** Use this when app code, local data, editor setup, test fixtures, and git credentials already live on your machine. - **Recipe prepared for local setup.** Use a recipe to create the Zerops service baseline, then run the agent locally from the directory that should own source changes. -After that choice, the mechanics are the same: install `zcp`, run `zcp init`, add `ZCP_API_KEY`, start VPN when private service access is needed, and link a deploy target when the agent should deploy. +After that choice, the mechanics are the same: install `zcp`, run `zcp init`, add `ZCP_API_KEY`, start VPN when private service access is needed, and link a runtime when the agent should deploy. ## What local setup gives the agent - **Local files as source.** The agent edits the directory on your machine, and deploys use that working directory. - **Your editor and terminal.** Framework CLIs, test runners, local data, and local feedback stay under your normal tools. - **Your git credentials.** Pushes use your local git CLI, SSH agent, or credential helper. -- **Zerops operations.** ZCP lets the agent discover services, generate env snapshots, deploy to linked runtimes, read logs, and verify behavior. +- **Zerops operations.** The MCP tools let the agent discover services, generate env snapshots, deploy to linked runtimes, read logs, and verify. - **Private service access through VPN.** Your local app and shell reach private service hostnames through `zcli vpn up`. -Security note: local ZCP cannot protect your laptop from the agent client. Configure your local agent approvals the same way you would for any coding agent running on your machine. +Security note: local setup cannot protect your laptop from the agent client. Configure approvals as you would for any local coding agent. ## Prerequisites @@ -41,13 +41,11 @@ Security note: local ZCP cannot protect your laptop from the agent client. Confi - A **single-project Zerops token**. Multi-project tokens are refused at startup. - A local directory where the agent should run. -You do not need ZCP just to develop locally against Zerops services as a human. `zcli vpn up` plus your editor is enough for that. Add ZCP when a local coding agent should also understand and operate Zerops. +You do not need MCP just to develop locally against Zerops services as a human. `zcli vpn up` plus your editor is enough. Add MCP when a local coding agent should also understand and operate Zerops. ## 1. Get `ZCP_API_KEY` -ZCP needs a Zerops API token that reaches exactly one project. For normal agent work, use a full-access token for that project. Read-only tokens can authenticate but fail on deploys, env changes, lifecycle actions, and other mutations. - -Token generation, rejected shapes, and rotation are covered in [Tokens and credentials](/zcp/security/tokens-and-project-access). Keep the token value available for the `.mcp.json` step below. +The MCP server needs a Zerops API token that reaches exactly one project. For normal agent work, use a full-access token; read-only tokens can authenticate but fail on deploys, env changes, lifecycle actions, and other mutations. Token generation, rejected shapes, and rotation are covered in [Tokens and credentials](/zcp/security/tokens-and-project-access). ## 2. Install `zcp` @@ -71,17 +69,15 @@ From the local directory the agent should operate: zcp init ``` -`zcp init` writes the local MCP configuration and agent instructions for this directory. - -Current generated artifacts: +`zcp init` writes local MCP config and agent instructions: -- `.mcp.json` - MCP configuration your local agent client reads to discover ZCP. -- `CLAUDE.md` - agent instructions for operating ZCP here. +- `.mcp.json` - MCP server config for this directory. +- `CLAUDE.md` - agent instructions for Zerops work. - `.claude/settings.local.json` - Claude Code per-project settings when that client is used. -- `~/.config/zerops/aliases` plus a shell-rc sourcing line - helper aliases for launching the agent from this directory. -- `.zcp/state/` - created later when ZCP first writes local state. +- `~/.config/zerops/aliases` plus a shell-rc sourcing line - helper aliases for launching the agent here. +- `.zcp/state/` - workflow state created when MCP first writes local state. -Re-running `zcp init` may refresh generated config. `CLAUDE.md` preserves edits outside managed markers, but `.mcp.json` is regenerated from the token-less template. If you rerun it, re-check the `ZCP_API_KEY` env block before launching the agent again. +Re-running `zcp init` may refresh generated config. `CLAUDE.md` preserves edits outside managed markers, but `.mcp.json` is regenerated from the token-less template. If you rerun it, re-check the `ZCP_API_KEY` block before launching the agent. ## 4. Add `ZCP_API_KEY` @@ -103,50 +99,43 @@ Re-running `zcp init` may refresh generated config. `CLAUDE.md` preserves edits Add `.mcp.json` to `.gitignore`. It contains a live Zerops credential and should not leave the machine. Each app directory should have its own `.mcp.json` and token. -The server name `zerops` is intentional. Do not rename that key unless your agent client explicitly requires a different name and you understand the resulting prompt/instruction changes. +The server name `zerops` is intentional. Do not rename it unless your agent client requires a different name and you understand the prompt/instruction changes. ## 5. Launch the agent from the app directory -Start your local agent client from the same directory that contains `.mcp.json`. The client should list `zerops` as an available MCP server. +Start the agent from the directory that contains `.mcp.json`. The client should list `zerops` as an available MCP server. Sanity check: ```text -Use ZCP to list the Zerops services. +List the Zerops services through MCP. ``` -A working connection answers with the runtime and managed services. If the agent cannot reach ZCP, check the launch directory, token scope, and whether the client loaded `.mcp.json`. +A working connection answers with the runtime and managed services. If not, check the launch directory, token scope, and whether the client loaded `.mcp.json`. ## 6. Bring up VPN when private services are needed -ZCP can talk to the Zerops API without VPN. Your local app, shell, tests, database clients, and framework commands need VPN to reach private service hostnames such as `db` or `cache`. +The MCP server can talk to the Zerops API without VPN. Your local app, shell, tests, database clients, and framework commands need VPN to reach private hostnames such as `db` or `cache`. ```bash zcli vpn up ``` -VPN setup needs admin or root approval on macOS and Linux. ZCP can tell the agent which command is needed, but it cannot approve or start the VPN for you. - -After the tunnel is up, service hostnames resolve from your machine. Laptop sleep, network changes, and idle time can drop the tunnel; run the command again when local service connections start failing. +VPN setup needs admin or root approval on macOS and Linux. The tools can tell the agent which command is needed, but they cannot approve or start it for you. After the tunnel is up, service hostnames resolve from your machine; rerun the command when local service connections fail. ## 7. Generate local env when needed -When the local app needs service credentials, ask the agent for the app work and let ZCP generate the env bridge as part of that work. For a standalone check: +When the local app needs service credentials, ask the agent for the app work and let the tools generate the env bridge. For a standalone check: ```text -Use ZCP to generate a .env file for my local app. +Generate a .env file for my local app from Zerops env references. ``` -Env generation needs a real local deploy contract: - -- a `zerops.yaml` in the working directory, -- the runtime or setup the local app should use, -- a matching `setup:` entry in `zerops.yaml`, -- non-empty `run.envVariables` under that setup. +Env generation needs `zerops.yaml` in the working directory, the runtime or setup the local app should use, a matching `setup:` entry, and non-empty `run.envVariables` under that setup. -ZCP reads `run.envVariables`, resolves Zerops references such as `${db_user}`, `${db_password}`, and `${db_hostname}`, and writes the resulting values into `.env`. Cross-service references resolve recursively, so `DATABASE_URL=postgres://${db_user}:${db_password}@${db_hostname}:${db_port}/${db_dbName}` lands as a complete local connection string. +The tools read `run.envVariables`, resolve Zerops references such as `${db_user}`, `${db_password}`, and `${db_hostname}`, and write the resulting values into `.env`. Cross-service references resolve recursively, so a `DATABASE_URL` can land as a complete local connection string. -If those inputs are missing, the agent should fix the local `zerops.yaml` or ask for the target runtime/setup. It should not invent env values from service names or dashboard memory. +If those inputs are missing, the agent should fix `zerops.yaml` or ask for the target runtime/setup. It should not invent env values from service names or dashboard memory. The file is a snapshot, not a live sync. Regenerate it after changing env variables in Zerops. VPN is still required for your local app to use private service hostnames from that `.env`. @@ -156,34 +145,32 @@ Keep `.env` out of git. It contains real connection values. Local setup needs a Zerops runtime when the agent should deploy from your working directory. A stage runtime is the usual target because it lets local work prove itself before production promotion. -If there is exactly one runtime, ZCP can use it automatically. If multiple runtimes exist, the agent should ask which one to link. Answer by hostname, for example: +If there is exactly one runtime, the agent can use it automatically. If multiple runtimes exist, it should ask which one to link: ```text Link this local directory to appstage for deploys. ``` -Without a linked runtime, ZCP can still inspect services and generate env snapshots, but deploys from your local directory need a target first. +Without a linked runtime, the tools can still inspect services and generate env snapshots, but local deploys need a target first. -## What stays outside ZCP +## What stays outside -ZCP does not replace your local feedback loop. Vite, Valet, Docker Compose, your IDE runner, framework CLIs, local fixtures, and test data stay under your normal local tooling. +MCP does not replace your local feedback loop. Vite, Valet, Docker Compose, your IDE runner, framework CLIs, local fixtures, and test data stay under your normal tooling. -ZCP does not mount Zerops runtime filesystems on your laptop. Remote setup can mount runtime files into the workspace; local setup works from files on your machine. If you need to inspect runtime files from your machine, use [SSH](/references/networking/ssh) directly. +Local setup does not mount Zerops runtime filesystems on your laptop. Remote setup can mount runtime files into the workspace; local setup works from files on your machine. To inspect runtime files from your machine, use [SSH](/references/networking/ssh) directly. -ZCP does not own your git credentials. In local setup, your local git CLI, SSH agent, or credential helper handles pushes. +The MCP server does not own your git credentials. In local setup, your local git CLI, SSH agent, or credential helper handles pushes. ## Local setup checks -- **Launch directory matters.** Start the agent from the folder that contains `.mcp.json`; otherwise it may connect to no ZCP server or the wrong token. -- **Multi-project tokens are rejected.** Use a token scoped to exactly one project. +- **Launch directory matters.** Start the agent from the folder that contains `.mcp.json`. - **`zcp init` can remove the token from `.mcp.json`.** Re-add `ZCP_API_KEY` after rerunning init. -- **VPN is separate from ZCP auth.** The agent may reach ZCP while your app still cannot reach `db` because VPN is down. +- **VPN is separate from MCP auth.** MCP may work while your app still cannot reach `db`. - **`.env` generation depends on `run.envVariables`.** If generation fails, check that the working directory has `zerops.yaml`, the selected `setup:` exists, and env entries live under `run.envVariables`. - **`.env` is a credential snapshot.** Regenerate after env changes and keep it out of git. -- **Production stays out of this loop.** Local ZCP is for development and staging. ## Next steps -- [Remote or local ZCP setup](/zcp/setup/choose-workspace) - compare setups. -- [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. +- [Remote or local setup](/zcp/setup/choose-workspace) - compare setups. +- [Build and ship](/zcp/workflows/build-with-zcp) - normal app work after setup. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and destructive confirmations. 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 647b5323c..1732eac91 100644 --- a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx +++ b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx @@ -1,11 +1,11 @@ --- -title: 'Moved: Build with ZCP' -description: 'Development and verification are now covered in Build with ZCP.' +title: 'Moved: Build and ship' +description: 'Development and verification are now covered in Build and ship.' unlisted: true --- -This content now lives in [Build with ZCP](/zcp/workflows/build-with-zcp). +This content now lives in [Build and ship](/zcp/workflows/build-with-zcp). -Use that guide for the user-facing development controls. Use [How ZCP works -> Verification has two layers](/zcp/concept/how-it-works#verification-has-two-layers) for the deeper verification model. +Use that guide for the user-facing development controls. Use [How it works -> Verification has two layers](/zcp/concept/how-it-works#verification-has-two-layers) for the deeper verification model. For symptoms and recovery moves, use [Troubleshooting](/zcp/reference/troubleshooting). diff --git a/apps/docs/content/zcp/workflows/build-with-zcp.mdx b/apps/docs/content/zcp/workflows/build-with-zcp.mdx index c58ee397e..3a0f4fad5 100644 --- a/apps/docs/content/zcp/workflows/build-with-zcp.mdx +++ b/apps/docs/content/zcp/workflows/build-with-zcp.mdx @@ -1,27 +1,20 @@ --- -title: 'Build with ZCP' -description: 'How to give ZCP app work: runtime layout, live context, acceptance criteria, and delivery preference.' +title: 'Build and ship' +description: 'The practical choices you control when a coding agent works on a Zerops project.' --- -Build with ZCP starts from product intent, not from an operations checklist. +Use Build and ship for the decisions that shape normal app work. ZCP MCP gives the agent Zerops project state, platform guidance, project-scoped operations, deploy evidence, and recovery rules. You still decide what should be built, where it should run, how strict the acceptance criteria are, and what happens after proof. ```text Build a task board. Tasks should stay saved after refresh. ``` -The example is short so the ZCP work stays visible: service discovery, runtime fit, Zerops wiring, deploy, verification, evidence reading, recovery, and continuation from current state. +A prompt can be that short when the outcome is enough. Add detail when it changes behavior, architecture, stack, runtime layout, acceptance criteria, credentials, delivery, packaging, or production promotion. -Short does not mean one-shot or shallow. The same loop can carry detailed engineering work: stack choices, architecture constraints, multi-step acceptance criteria, existing-code inspection, delivery preferences, external credentials, and follow-up sessions over the same Zerops environment. +The expected output is a verified running change, not only generated files. The agent should prove the request against a real runtime, real managed services when used, and the logs, events, and checks that explain what happened. -The expected output is a verified running change, not only generated files. ZCP should prove the request against a real runtime, real managed services when the task uses them, and the logs, events, and checks that explain what happened. - -:::note Prompt depth - -Start short when the outcome is enough. Add detail when it changes product behavior, architecture, stack, runtime layout, acceptance criteria, delivery, credentials, or safety approvals. ZCP handles the Zerops work behind both short and detailed prompts. -::: - -## The lifecycle +## The decisions
@@ -32,96 +25,105 @@ Start short when the outcome is enough. Add detail when it changes product behav

1. Runtime layout

-

Settle the app runtime and dependencies.

+

Choose where app work should land.

    -
  • Existing services
  • -
  • Missing services
  • -
  • Layout choice
  • +
  • Use existing services
  • +
  • Create missing services
  • +
  • Pick dev, stage, or linked target
-

2. Development context

-

Give code-facing work live state.

-
    -
  • Live state
  • -
  • Relevant knowledge
  • -
  • Deploy + verify
  • -
+

2. Development

+

The agent gets current state, Zerops knowledge, deploy evidence, and your product instructions.

-

3. Delivery preference

-

Close app work after proof or a clear blocker.

+

3. After proof

+

Decide what happens to verified work.

    -
  • Proof first
  • -
  • Keep direct deploy
  • -
  • Git, CI, or handoff
  • +
  • Direct deploy, git, or CI
  • +
  • Optional package bundle
  • +
  • Production promotion
-The controls that matter are the runtime layout you want, the product or code change you ask for, and the delivery preference after proof. You do not run the lifecycle by hand; ZCP uses it to give the agent context and done criteria. +You do not run these steps by hand. They are the parts where your intent changes what the agent should do. If you do not specify a choice, the agent should infer from current project state and ask only when the decision changes cost, credentials, runtime layout, delivery, production risk, or destructive behavior. -## 1. Settle the runtime layout {#choose-the-runtime-layout} +## 1. Choose the runtime layout {#choose-the-runtime-layout} -When the app runtime and dependencies are unclear, ZCP prepares the layout before feature work starts. It reads current state, uses existing services when they fit, creates missing app runtimes or managed services when needed, and stops when the agent knows where app code belongs. +When the app runtime and dependencies are unclear, the workflow prepares the layout before feature work starts. It reads current state, uses existing services when they fit, creates missing runtimes or managed services when needed, and stops when the agent knows where app code belongs. -The user-facing choice that most affects this first step is the runtime layout. You can let ZCP infer it from the project, or you can name the layout in the prompt when it matters. +The main user-facing choice is runtime layout. Let the agent infer it from the project, or name it in the prompt when it matters. | Runtime layout | Use when | What to tell the agent | | ------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------- | | **Dev** | You want one mutable runtime for fast iteration, experiments, or early app work. | `Build a small Node.js API on dev.` | | **Dev + stage** | You want a development runtime plus a separate runtime for review or promotion rehearsal. | `Build a Node.js API with PostgreSQL on dev+stage.` | -| **Stage / linked target** | You work from local files or a single target runtime and want ZCP to use that target. | `Use appstage as the deploy target for this local app.` | - -If you do not specify a layout, the agent should infer it from current state and ask only when the choice changes cost, runtime layout, stage target, credentials, or a destructive action. +| **Stage / linked target** | You work from local files or a single target runtime and want the workflow to use that target. | `Use appstage as the deploy target for this local app.` | -The layout is about app runtimes, not where ZCP itself runs. Remote setup can use any of these layouts from the `zcp@1` workspace. Local setup usually works from local files into a linked stage or app runtime. +The layout is about app runtimes, not where the `zcp` binary runs. Remote setup can use any of these layouts from the `zcp@1` workspace. Local setup usually works from local files into a linked stage or app runtime. -Managed services such as PostgreSQL, Valkey, queues, search, storage, or mail are dependencies. ZCP gives the agent their state and wiring patterns, but app code deploys to runtime services. +Managed services such as PostgreSQL, Valkey, queues, search, storage, or mail are dependencies. The agent gets their state and wiring patterns, but app code deploys to runtime services. -## 2. Develop with live project context {#develop-with-live-project-context} +## 2. Development {#develop-with-live-project-context} -Any instruction that touches code starts or continues app development: build, inspect, change, fix, continue, deploy, or verify. ZCP adds the Zerops context the agent needs, but it does not replace the normal coding conversation between you and the agent. +Development is still a normal coding conversation with the agent. You describe the product behavior, stack constraints, acceptance criteria, and anything the agent must not guess. -Inside app development, the agent can still read files, make implementation choices, run tools, and follow your product instructions. ZCP matters around that work: it supplies current state, relevant Zerops knowledge, bounded operations, deploy evidence, verification checks, and recovery rules. +MCP adds the Zerops side of that conversation: current project state, platform knowledge, project-scoped operations, deploy/log evidence, verification checks, recovery rules, and saved work state. Because of that, you usually do not paste service inventory, env wiring, deploy logs, or a "deploy and verify" checklist into the prompt. -| What the agent gets from ZCP | Why it matters during development | -| ---------------------------- | ---------------------------------------------------------------------------------------------------- | -| Live state | Services, runtime layout, env-var keys, Zerops references, events, deploys, logs, and saved work. | -| Zerops knowledge | Runtime and managed-service wiring, `zerops.yaml`, env refs, ports, public access, and deploy rules. | -| Bounded operations | Service setup, env vars, deploys, logs, lifecycle, verification, and recovery. | -| Done criteria | When to use existing services, create missing ones, read evidence, ask, or report a blocker. | +The useful things to name are: -That means you usually do not paste an infrastructure inventory, env wiring plan, or log summary into the chat. A short product prompt can be enough because the agent can ask what exists now. +- the behavior you want and how it should be verified, +- stack, framework, managed-service, or runtime target preferences, +- whether the agent should inspect or continue existing work first, +- approval boundaries for cost, credentials, data, production, or destructive actions. -What you control here is the product request, stack preference, acceptance criteria, and any instruction to inspect or continue existing work. ZCP gives the agent the deploy, verification, evidence reading, and recovery loop for that request. +Expect the agent to ask when one of those choices is missing. Otherwise, it should use project context while it works and finish with proof or a blocker. -Expect the agent to stop for external credentials, destructive actions, cost-affecting infrastructure, ambiguous runtime or stage choices, and product decisions it cannot infer. - -| Development input | Use when | What to tell the agent | -| ----------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------- | -| **Product behavior** | The feature or app outcome matters more than the exact stack. | `Build a task board where tasks stay saved after refresh.` | -| **Stack preference** | You want a specific runtime, framework, or managed service. | `Use Laravel and the existing PostgreSQL service.` | -| **Acceptance criteria** | A specific behavior must be true before the task is done. | `A user can create a task, refresh the page, and still see it.` | -| **Runtime layout** | The target layout matters for the work or review target. | `Build a Node.js API with PostgreSQL on dev+stage.` | -| **Existing work** | A previous ZCP session stopped or the app already exists. | `Read ZCP status first, then continue the interrupted task board work.` | - -## 3. Choose delivery preference {#choose-delivery-after-proof} +## 3. Choose delivery after proof {#choose-delivery-after-proof} Delivery preference is how app work closes after there is a verified result. Include it in the original prompt or set it later. -The first functional deploy is still direct so the agent can prove the app actually runs. Delivery preference decides what happens after that proof and how later ZCP sessions should finish similar work. +The first functional deploy is still direct so the agent can prove the app runs. Delivery preference decides what happens after that proof and how later sessions should finish similar work. | Delivery preference | What it means | What to tell the agent | | ---------------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | -| **Keep direct deploy** | ZCP keeps deploying directly to the target runtime for fast dev/stage iteration. | `Keep direct deploy for now.` | +| **Keep direct deploy** | The agent keeps deploying directly to the target runtime for fast dev/stage iteration. | `Keep direct deploy for now.` | | **Push to git** | The agent commits and pushes working changes to the configured repository. | `When the app works, push changes to git@github.com:my-org/task-board.git.` | | **CI / handoff** | A repository integration, GitHub Actions workflow, release process, or human owns the next deploy. | `Set up GitHub Actions delivery for future deploys after the app works.` | -ZCP records the delivery choice so later work can follow it without re-explaining the handoff. Git credentials, CI secrets, and production credentials are separate from `ZCP_API_KEY`; see [Tokens and credentials](/zcp/security/tokens-and-project-access). +The workflow records the delivery choice so later work can follow it. Git credentials, CI secrets, and production credentials are separate from `ZCP_API_KEY`; see [Tokens and credentials](/zcp/security/tokens-and-project-access). + +## 4. Package a verified runtime {#package-a-verified-runtime} + +After a runtime is verified, you can ask the agent to prepare it as a re-importable Zerops project bundle. Use this when the app should become a reusable starter, customer handoff, demo project, or clean staging project. + +The prompt can be one sentence: -Production should still be a separate Zerops project. ZCP can prepare the handoff from dev or stage, but production promotion belongs to your release process; see [Production boundary](/zcp/security/production-policy). +```text +Package appstage as a buildFromGit import bundle, commit it, and push it to git so I can import it into a fresh Zerops project. +``` + +The export workflow prepares `zerops-project-import.yaml` and `zerops.yaml` in the same git repo as the app. The import file contains one runtime with `buildFromGit:` pointing back to that repo, plus managed services needed for Zerops env references to resolve when the bundle is imported into a fresh project. + +The agent may still ask which runtime to package, which half of a dev+stage pair to use, how to classify project env vars, or how to configure git push. Once pushed, the target project can import the bundle from the dashboard or with `zcli project project-import zerops-project-import.yaml`. + +Packaging is not the next deploy of the same app. For the next app change, keep using the normal build/deploy/verify loop. Use packaging when the output you want is a git-backed import bundle; see [Package a running service](/zcp/workflows/package-running-service). + +## 5. Prepare production promotion {#prepare-production-promotion} + +Production promotion is where authority changes. The agent prepares verified work and release evidence; production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. + +The useful split is: + +| Job | How often | What happens | +| --- | --- | --- | +| **Production infrastructure** | Once per production project | Export the verified project as YAML in the GUI, edit it for production, then import it as a new project. | +| **Production deploy trigger** | Once per production runtime | Connect the production runtime to git, usually with a tag trigger. | +| **Production release** | Every release | Verify in dev/stage, push source to git, then trigger production through your tag or release process. | + +Production should be a separate Zerops project without a `zcp` service. Production credentials are not `ZCP_API_KEY`; keep them in CI or release tooling. For the full release guide, see [Promote to production](/zcp/workflows/promote-to-production). ## What the final answer should contain @@ -131,16 +133,15 @@ For a completed app task, the agent should report: - the deploy or verification target, - the URL, endpoint, UI state, job result, or stored data that proves the requested behavior, - managed services, env vars, or delivery settings it touched, -- the delivery preference that now applies. +- the delivery preference, packaging output, or production promotion state that now applies. If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the decision or credential needed from you. -Advanced reuse: [Package a running service](/zcp/workflows/package-running-service) turns a verified runtime into a re-importable bundle. It is not the normal lifecycle for the next app change. - ## Next steps diff --git a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx index d8685c028..ca3774724 100644 --- a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx +++ b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx @@ -1,10 +1,10 @@ --- -title: 'Moved: Build with ZCP' -description: 'Infrastructure setup is now covered in Build with ZCP.' +title: 'Moved: Build and ship' +description: 'Infrastructure setup is now covered in Build and ship.' unlisted: true --- -This content now lives in [Build with ZCP](/zcp/workflows/build-with-zcp). +This content now lives in [Build and ship](/zcp/workflows/build-with-zcp). Use that guide for runtime layout, existing services, missing services, and the handoff from setup into app work. diff --git a/apps/docs/content/zcp/workflows/delivery-handoff.mdx b/apps/docs/content/zcp/workflows/delivery-handoff.mdx index 069f63525..37affb85b 100644 --- a/apps/docs/content/zcp/workflows/delivery-handoff.mdx +++ b/apps/docs/content/zcp/workflows/delivery-handoff.mdx @@ -1,10 +1,10 @@ --- -title: 'Moved: Build with ZCP' -description: 'Delivery preference is now covered in Build with ZCP.' +title: 'Moved: Build and ship' +description: 'Delivery preference is now covered in Build and ship.' unlisted: true --- -This content now lives in [Build with ZCP](/zcp/workflows/build-with-zcp). +This content now lives in [Build and ship](/zcp/workflows/build-with-zcp). Use that guide for the three user-facing delivery choices: keep direct deploy, push verified work to git, or hand off to CI/release tooling. diff --git a/apps/docs/content/zcp/workflows/package-running-service.mdx b/apps/docs/content/zcp/workflows/package-running-service.mdx index 309b5109f..9d2df5017 100644 --- a/apps/docs/content/zcp/workflows/package-running-service.mdx +++ b/apps/docs/content/zcp/workflows/package-running-service.mdx @@ -1,24 +1,31 @@ --- title: 'Package a running service' -description: 'Use ZCP to turn a running Zerops service into a re-importable bundle.' +description: 'Turn a running Zerops service into a re-importable bundle.' --- -Use packaging when a deployed runtime should become a reusable import bundle. This is an advanced handoff or reuse task, not the normal way to deploy the next app change. +Use packaging when a deployed runtime should become a reusable import bundle. This is a handoff or reuse task after a runtime already works, not the normal way to deploy the next app change. -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. The import file points back to the same source repo through `buildFromGit:`, so a fresh Zerops project can rebuild the app from git instead of carrying source code inside YAML. +You can ask for it directly: + +```text +Package appstage as a buildFromGit import bundle, commit it, and push it to git so I can import it into a fresh Zerops project. +``` + +ZCP's export workflow takes a deployed runtime and prepares a single-repo bundle: source code, a project [import file](/references/import) named `zerops-project-import.yaml`, and a [`zerops.yaml`](/zerops-yaml/specification). The import file points back to the same source repo through `buildFromGit:`, so a fresh Zerops project can rebuild the app from git instead of carrying source code inside YAML. ## 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. -- You want the new project to build from the same git repo, not from a copy of the code in YAML. +- You want a re-importable snapshot committed next to the app source. +- You want the new project to build from the same git repo through `buildFromGit:`, not from a copy of the code in YAML. +- You want to prepare a reusable starter, demo, handoff, or clean staging project. Packaging is **not** the right tool when: - You want the next app change deployed. For that, ask for the app result; direct deploy is the expected ZCP behavior behind the request. -- You want to deploy the next change to an existing production project. Keep production in its own Zerops project and operate it through Zerops UI, the API, or CI. +- You want to create or update a production project. For production, export/import the project infrastructure and connect a production git or CI trigger; see [Promote to production](/zcp/workflows/promote-to-production). Packaging does not keep a long-running session. If interrupted, the agent reads live state and prepares the bundle again. You only need to decide when the runtime choice or env-var classification is ambiguous. @@ -31,10 +38,10 @@ Managed services (`db`, `redis`, etc.) come along automatically as dependencies; 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. +Package appstage as a buildFromGit import bundle, commit it, and push it so I can import it into a fresh Zerops project. ``` -The agent handles the packaging work; you only step in if it asks which runtime to package or how to classify a value. +The agent handles the packaging work; you only step in if it asks which runtime to package, which half of a dev+stage pair to use, how to classify a value, or how to configure git push. ## Classify env vars @@ -61,7 +68,7 @@ Auto-secret rotation is destructive to any persisted state encrypted with the ol ## What the bundle contains -A successful run produces a **single-repo, self-contained bundle** with two files: +A successful run produces a **single-repo, self-contained bundle**. The app source stays in git, and the export workflow adds or verifies two files at the repo root: ```yaml #zeropsPreprocessor=on @@ -113,12 +120,16 @@ zerops: A few things worth noticing: -- The first line is the [Zerops preprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `<@...>` directives (here `<@generateRandomString(<32>)>` for `APP_KEY`), `#zeropsPreprocessor=on` must be line 1. Without it, the platform skips directive expansion at re-import and the literal string lands in the env var. ZCP adds this automatically when any directive is present. +- The first line is the [Zerops preprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `<@...>` directives (here `<@generateRandomString(<32>)>` for `APP_KEY`), `#zeropsPreprocessor=on` must be line 1. Without it, the platform skips directive expansion at re-import and the literal string lands in the env var. The export workflow adds this 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). Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen delivery preference expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through normal [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). +After the bundle is pushed, import `zerops-project-import.yaml` in the destination project through the dashboard or `zcli project project-import zerops-project-import.yaml`. The runtime entry in that import file uses `buildFromGit:` to clone and build the same repo. + +For production setup, prefer the production promotion flow. Packaging creates a reusable app bundle; it does not decide production infrastructure, domains, secrets, release triggers, or scaling. See [Promote to production](/zcp/workflows/promote-to-production). + ## What the bundle does not include | Not in the bundle | Where it lives instead | @@ -126,16 +137,16 @@ Packaging can be part of a handoff intent. The agent should produce the files, p | 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 | +| Production infrastructure, domains, and release triggers | Set up in the production Zerops project | ## Packaging checks -1. **Packaging is not delivery by itself.** It produces the bundle. Commit and push happen through your chosen [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). +1. **Packaging is not delivery by itself.** It produces the bundle. Commit and push happen through your chosen [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) or the explicit packaging prompt. 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 -- [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) - choose how the bundle's two files ship. -- [How ZCP works](/zcp/concept/how-it-works#verification-has-two-layers) - the verified runtime this packages. -- [How ZCP works](/zcp/concept/how-it-works) - why packaging stays stateless. +- [Build and ship](/zcp/workflows/build-with-zcp#package-a-verified-runtime) - where packaging fits into normal agent work. +- [How it works](/zcp/concept/how-it-works#verification-has-two-layers) - the verified runtime this packages. +- [Promote to production](/zcp/workflows/promote-to-production) - create production infrastructure and release from git. diff --git a/apps/docs/content/zcp/workflows/promote-to-production.mdx b/apps/docs/content/zcp/workflows/promote-to-production.mdx new file mode 100644 index 000000000..74acbd9a0 --- /dev/null +++ b/apps/docs/content/zcp/workflows/promote-to-production.mdx @@ -0,0 +1,114 @@ +--- +title: 'Promote to production' +description: 'Move verified dev or stage work into a production Zerops project without putting the zcp service in production.' +--- + +Use this after the agent has proved work in development or stage. Production promotion is the point where authority changes: the agent can prepare proof, source changes, and setup notes, but production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. + +Production should be a separate Zerops project without a `zcp` service. That keeps the development agent out of the production blast radius while still letting verified work reach production. + +## The simple production path + +There are three different jobs. Two are setup work; one is the repeatable release operation. + +| Job | How often | Simplest path | +| --- | --- | --- | +| **Create production infrastructure** | Once per production project | Export the verified Zerops project as YAML in the GUI, edit it for production, then import it as a new project. | +| **Connect the production deploy trigger** | Once per production runtime | In the production project, connect the git repository in the Zerops GUI and trigger builds from release tags. | +| **Release an app change** | Every release | Verify in dev/stage, push source to git, then create the release tag or use your team's release process. | + +This page uses the GUI path because it is the clearest path available today. More customized teams can replace the GUI trigger with GitHub Actions or another CI system later. + +## 1. Create production infrastructure + +Do this once when you need a production project for a verified app. + +1. Open the development or staging project that contains the verified runtime. +2. Use **Export project as yaml**. +3. Edit the exported YAML for production. +4. Import the edited YAML as a new Zerops project. + +Before import, review the YAML as production infrastructure, not as a blind copy of the development project: + +- remove the `zcp` service, +- remove dev-only runtimes and tools such as Mailpit or Adminer, +- keep the runtime and managed services that production actually needs, +- choose production `mode` values before creation, especially `HA` for databases that need it, +- set production `minContainers`, autoscaling, and core package, +- replace development secrets with production secrets or placeholders, +- plan managed-service data restore or migrations, +- configure production domains, DNS, public access, SMTP, object storage, backups, queues, search, and cache as needed. + +Before opening production traffic, get familiar with the deeper Zerops production surface: service modes, scaling, deploy pipeline, health checks, backups, public access, and domains. Start with the [Production Checklist for Zerops](/guides/production-checklist). If the app should use a real domain, follow [Public Access Configuration](/references/networking/public-access). + +Project export/import creates the production infrastructure. Do not rely on it as the code delivery mechanism. The app source should be in git, and the first production code deploy should come through the production deploy trigger. + +## 2. Connect the production deploy trigger + +Do this once for each production runtime that should build from git. + +The simplest path is the Zerops GitHub or GitLab integration in the production project: + +1. Open the production runtime service. +2. Connect the git repository. +3. Choose **New tag** as the production trigger. +4. Add a tag filter if your team uses one, for example `v*`. +5. Keep production env vars and secrets in the production Zerops project. + +For stage, a branch trigger can be convenient. For production, a tag trigger is easier to reason about: a normal source push can update stage, while a deliberate release tag updates production. + +If your team needs approvals, custom tests, generated artifacts, or stricter audit, use GitHub Actions or another CI system instead. In that setup, store a production-scoped `ZEROPS_TOKEN` in the CI secret store and run the production deploy from CI. + +## 3. Release an app change + +This is the repeatable operation after production infrastructure and the deploy trigger exist. The exact release command is yours: define it in your repository instructions, agent skill, CI guide, or team release checklist. + +For example, if production deploys from release tags, the user instruction can be as direct as: + +```text +Create the release tag for production and push it to git. +``` + +Use the wording that matches your CI/CD. The release step can be a manual CLI action, a `git push`, a tag push, a pull request, a protected branch merge, or a CI job. That part is your team preference and production setup, not a rule from these docs. + +The important rule is that the agent should name what it pushed and whether that push triggers stage, production, both, or neither. If production release requires a tag, the final answer should say the tag name or the exact tag command. + +## What not to carry into production + +Do not copy these from the development project into production: + +- the `zcp` service, +- development `ZCP_API_KEY`, +- local `.mcp.json`, +- development secrets, +- test data unless it is intentionally migrated, +- dev-only utilities such as Mailpit or Adminer, +- `.zerops.app` preview routing as the final public entry point. + +Use production-specific domains, secrets, backup policy, scaling, and release credentials. + +## What the agent should hand you + +A useful release handoff from the agent contains: + +- runtime and project where the change was verified, +- URL, endpoint, UI state, job result, or stored data that proves the requested behavior, +- commit, branch, PR, or suggested release tag, +- whether the git push triggers stage, production, both, or neither, +- services to keep, remove, or change before importing production infrastructure, +- env vars, managed services, migrations, or delivery settings touched, +- external secrets still needed, +- managed-service data that must be restored or migrated, +- production blockers that need a human decision. + +If the answer only says that files were changed, the release handoff is incomplete. Ask for the verification evidence and the exact release artifact. + +Production hardening details live in the [Production Checklist for Zerops](/guides/production-checklist). This page only defines the handoff from verified dev or stage work into production authority. + +## Next steps + +- [Build and ship](/zcp/workflows/build-with-zcp#prepare-production-promotion) - where production promotion fits into normal agent work. +- [Production boundary](/zcp/security/production-policy) - why production stays outside the agent loop. +- [CI/CD with Zerops](/guides/ci-cd) - delivery options for production release paths. +- [Import & Export YAML Configuration](/references/import) - project export and import reference. +- [Public Access Configuration](/references/networking/public-access) - custom domains, DNS, SSL, and production public access. diff --git a/apps/docs/sidebars.js b/apps/docs/sidebars.js index 43d7fea3c..d7485e949 100644 --- a/apps/docs/sidebars.js +++ b/apps/docs/sidebars.js @@ -152,7 +152,7 @@ module.exports = { { type: 'doc', id: 'features/coding-agents', - label: 'ZCP for Coding Agents', + label: 'Infrastructure for Coding Agents', customProps: { sidebar_icon: 'sparkles', }, @@ -578,7 +578,7 @@ module.exports = { }, { type: 'category', - label: 'Zerops Control Plane', + label: 'Zerops Control Plane MCP', collapsible: false, customProps: { sidebar_is_group_headline: true, @@ -605,7 +605,7 @@ module.exports = { { type: 'doc', id: 'zcp/concept/how-it-works', - label: 'How ZCP works', + label: 'How it works', customProps: { sidebar_icon: 'cog-six-tooth', }, @@ -631,18 +631,33 @@ module.exports = { { type: 'doc', id: 'zcp/setup/local-agent-bridge', - label: 'How to run ZCP on your machine', + label: 'Run locally', }, ], }, { - type: 'doc', - id: 'zcp/workflows/build-with-zcp', - label: 'Build with ZCP', + type: 'category', + label: 'Build and ship', + link: { + type: 'doc', + id: 'zcp/workflows/build-with-zcp', + }, customProps: { sidebar_icon: 'circle-stack', }, className: 'homepage-sidebar-item', + items: [ + { + type: 'doc', + id: 'zcp/workflows/package-running-service', + label: 'Package a running service', + }, + { + type: 'doc', + id: 'zcp/workflows/promote-to-production', + label: 'Promote to production', + }, + ], }, { type: 'category', @@ -688,12 +703,7 @@ module.exports = { { type: 'doc', id: 'zcp/reference/mcp-operations', - label: 'Advanced operations', - }, - { - type: 'doc', - id: 'zcp/workflows/package-running-service', - label: 'Package a running service', + label: 'MCP tools', }, { type: 'doc', diff --git a/apps/docs/src/css/custom.css b/apps/docs/src/css/custom.css index e37233c5d..7e4c124a9 100644 --- a/apps/docs/src/css/custom.css +++ b/apps/docs/src/css/custom.css @@ -312,6 +312,87 @@ html[data-theme='dark'] .docsearch-btn:hover { margin: 1.5rem 0 1.75rem; } +.zcp-agent-flow { + display: grid; + gap: 0.85rem; + grid-template-columns: 1fr; + margin: 1.25rem 0 1.75rem; +} + +.zcp-agent-flow__node { + background: #ffffff; + border: 1px solid #d7dce5; + border-radius: 8px; + color: #172033; + padding: 0.9rem; + position: relative; +} + +.zcp-agent-flow__node::after { + color: #6b7280; + content: '↓'; + font-size: 1.1rem; + font-weight: 700; + left: 50%; + line-height: 1; + position: absolute; + top: calc(100% + 0.08rem); + transform: translateX(-50%); +} + +.zcp-agent-flow__node:last-child::after { + content: none; +} + +.zcp-agent-flow__node span { + color: #6b7280; + display: block; + font-size: 0.68rem; + font-weight: 650; + letter-spacing: 0; + line-height: 1.1; + margin-bottom: 0.35rem; + text-transform: uppercase; +} + +.zcp-agent-flow__node strong { + display: block; + font-size: 0.9rem; + line-height: 1.25; +} + +.zcp-agent-flow__node p { + color: #4b5563; + font-size: 0.78rem; + line-height: 1.42; + margin: 0.45rem 0 0; +} + +.zcp-agent-flow__node--intent { + background: #f7fbff; + border-color: rgba(45, 114, 217, 0.35); +} + +.zcp-agent-flow__node--agent { + background: #fffaf0; + border-color: rgba(196, 127, 23, 0.35); +} + +.zcp-agent-flow__node--mcp { + background: #f4fbf6; + border-color: rgba(50, 132, 90, 0.35); +} + +.zcp-agent-flow__node--project { + background: #f8fafc; + border-color: rgba(75, 85, 99, 0.28); +} + +.zcp-agent-flow__node--proof { + background: #f7f5ff; + border-color: rgba(113, 87, 217, 0.35); +} + .zcp-lifecycle-prompt { align-items: center; background: #f7fbff; @@ -508,6 +589,35 @@ html[data-theme='dark'] .zcp-lifecycle-prompt code { color: #dbeafe; } +html[data-theme='dark'] .zcp-agent-flow__node { + color: #f8fafc; +} + +html[data-theme='dark'] .zcp-agent-flow__node span, +html[data-theme='dark'] .zcp-agent-flow__node p { + color: #cbd5e1; +} + +html[data-theme='dark'] .zcp-agent-flow__node--intent { + background: rgba(30, 58, 138, 0.22); +} + +html[data-theme='dark'] .zcp-agent-flow__node--agent { + background: rgba(120, 53, 15, 0.2); +} + +html[data-theme='dark'] .zcp-agent-flow__node--mcp { + background: rgba(20, 83, 45, 0.22); +} + +html[data-theme='dark'] .zcp-agent-flow__node--project { + background: rgba(24, 24, 27, 0.72); +} + +html[data-theme='dark'] .zcp-agent-flow__node--proof { + background: rgba(76, 29, 149, 0.22); +} + html[data-theme='dark'] .zcp-lifecycle-stage { color: #f8fafc; } @@ -539,6 +649,18 @@ html[data-theme='dark'] .zcp-choice-card li { } @media (min-width: 768px) { + .zcp-agent-flow { + gap: 0.95rem; + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + + .zcp-agent-flow__node::after { + content: '→'; + left: calc(100% + 0.22rem); + top: 50%; + transform: translateY(-50%); + } + .zcp-lifecycle-prompt { margin-left: 0; margin-right: auto; @@ -562,4 +684,5 @@ html[data-theme='dark'] .zcp-choice-card li { top: 50%; transform: translateY(-50%); } + } From 16f0728b9b2524478e79912ba2783b57c7291d6c Mon Sep 17 00:00:00 2001 From: krls2020 Date: Mon, 11 May 2026 16:43:45 +0200 Subject: [PATCH 16/24] Refine ZCP docs flow and reference pages --- .../docs/content/zcp/concept/how-it-works.mdx | 4 +- apps/docs/content/zcp/glossary.mdx | 118 ++++----- apps/docs/content/zcp/overview.mdx | 2 +- .../content/zcp/reference/agent-workflow.mdx | 242 +++++++++++------- apps/docs/content/zcp/reference/index.mdx | 6 +- .../content/zcp/reference/mcp-operations.mdx | 142 +++++----- .../content/zcp/reference/troubleshooting.mdx | 139 +++++----- .../content/zcp/setup/choose-workspace.mdx | 104 +++----- .../workflows/create-or-adopt-services.mdx | 4 +- .../zcp/workflows/delivery-handoff.mdx | 2 +- .../zcp/workflows/package-running-service.mdx | 161 ++++-------- apps/docs/sidebars.js | 2 +- apps/docs/src/css/custom.css | 54 ---- 13 files changed, 405 insertions(+), 575 deletions(-) diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx index f27e2c881..d8486b7c1 100644 --- a/apps/docs/content/zcp/concept/how-it-works.mdx +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -88,7 +88,7 @@ The generated workflow is opinionated about the concerns an agent must resolve d | Behavior proof | A done gate based on the requested behavior, not only on a successful build or reachable root URL. | The final answer can point to what was actually checked. | | Delivery handoff | Direct proof first; then git push, CI, or human handoff when that is the chosen delivery choice. | Shipping setup follows a verified running result. | -The exact labels for layouts, delivery modes, and setup routes live in the [Glossary](/zcp/glossary) and [Workflow terms](/zcp/reference/agent-workflow). +The exact labels for layouts, delivery modes, and setup routes live in the [Glossary](/zcp/glossary) and [Workflows in depth](/zcp/reference/agent-workflow). ## Service setup prepares the project layout @@ -187,5 +187,5 @@ That is the practical difference between "the agent wrote code" and "the app tas ## Where to go deeper - [Build and ship](/zcp/workflows/build-with-zcp) - normal app work after setup. -- [Workflow terms](/zcp/reference/agent-workflow) - exact runtime layouts, delivery terms, failure categories, and completion evidence. +- [Workflows in depth](/zcp/reference/agent-workflow) - process gates, generated files, runtime layouts, delivery terms, and completion evidence. - [Troubleshooting](/zcp/reference/troubleshooting) - practical recovery when a run gets stuck. diff --git a/apps/docs/content/zcp/glossary.mdx b/apps/docs/content/zcp/glossary.mdx index b60d02e01..d3ab11b31 100644 --- a/apps/docs/content/zcp/glossary.mdx +++ b/apps/docs/content/zcp/glossary.mdx @@ -1,84 +1,89 @@ --- title: 'Glossary' -description: 'Short definitions for ZCP MCP terms, exact workflow labels, setup names, delivery modes, and credential names.' +description: 'Short definitions for ZCP MCP, remote setup, local setup, workflow, delivery, production, and credential terms.' --- -Use the exact labels here when you are reading workflow status, writing agent policy, debugging a session, or reviewing a handoff. In normal prompts, describe the outcome you want. +Use these definitions when a page, workflow status, agent handoff, or policy needs exact wording. In normal prompts, describe the outcome you want. ## Core names **ZCP MCP** - Zerops Control Plane MCP: the MCP tool surface that exposes project-scoped Zerops operations to coding agents. -**ZCP MCP tools** - the bounded Zerops operations exposed to a coding agent. In MCP clients, this usually appears as the `zerops` server. - **MCP server** - the Model Context Protocol server exposed by the `zcp` binary. -**Agent client** - the editor, CLI, or hosted agent runtime that connects to ZCP MCP. +**ZCP MCP tools** - the bounded Zerops operations exposed to an agent or MCP-capable client. In MCP clients, this usually appears as the `zerops` server. -**`zcp` binary** - the executable. It runs inside remote setup or on your laptop in local setup. +**`zcp` binary** - the executable that can run inside remote setup or on your machine in local setup. **`zcp` service** - the service instance in a Zerops project that hosts remote setup. -**`zcp@1` service** - the Zerops service type behind remote setup. +**`zcp@1` service** - the Zerops service type used for remote setup. -**zCLI** - the Zerops command-line client for humans, scripts, VPN, and CI. It coexists with ZCP MCP. +**zCLI** - the Zerops command-line client for humans, scripts, VPN, and CI. It is separate from ZCP MCP. **zsc** - the in-container Zerops Setup Control utility used from `zerops.yaml`. -## Where ZCP MCP runs +## Setup and workspace **Remote setup** - the `zcp` binary running inside a Zerops `zcp@1` service. -**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use ZCP MCP tools. +**Include Coding Agent** - remote setup option that adds the bundled agent CLI, currently Claude Code, and preconfigures it to use ZCP MCP tools. **Cloud IDE** - browser-based VS Code served by remote setup. -**Browser VS Code** - the dashboard entry point into the Cloud IDE. - -**AI Agent environment** - a recipe environment preset that creates app services plus remote setup with **Include Coding Agent** enabled. It is a recipe preset, not a separate ZCP MCP setup choice. +**Browser VS Code** - dashboard entry point into the Cloud IDE. -**Local setup** - the `zcp` binary running on your laptop after `zcp init`, while your editor or CLI agent talks to it. +**AI Agent environment** - recipe environment preset that creates app services plus remote setup with **Include Coding Agent** enabled. -**Local agent bridge** - local setup with the `zcp` binary connected to a local agent client. +**Local setup** - the `zcp` binary running on your machine after `zcp init`, while your local editor or CLI agent talks to it. **Env bridge** - local setup behavior that writes a local `.env` snapshot from Zerops env vars and references so local app code can reach managed services over VPN. -## Work phases and loops +**Agent client** - the editor, CLI, hosted agent runtime, or custom MCP client that connects to ZCP MCP. -**Service setup** - the infrastructure phase behind an app prompt. It uses existing runtime and managed services when they fit, creates missing services when needed, then stops before app code, `zerops.yaml`, or deploy. +## Generated files and state -**Infrastructure setup** - user-facing synonym for service setup. +**Generated workflow block** - the managed section in `CLAUDE.md` between `` and ``. Durable project instructions belong outside it. -**Setup route** - exact reference label for how service setup starts: +**`.mcp.json`** - local MCP server config. It points the local agent client at `zcp serve` and stores `ZCP_API_KEY`; keep it out of git. -- `adopt` - runtime services already exist and should be used. -- `recipe` - an empty or ZCP-only project matches a known recipe/stack. -- `classic` - an empty project needs a custom service plan. -- `resume` - an interrupted setup should continue from saved state and live services. +**`.zcp/state/`** - local workflow metadata for a project directory: known runtimes, pairing, delivery choice, sessions, deploy attempts, verify attempts, and coordination locks. It is not source code. -**Deploy, verify, and fix** - the app phase. It scopes the runtime, changes code/config, deploys, verifies reachability, verifies requested behavior, and fixes failures from evidence. +**Workflow state** - saved metadata that lets the agent resume, audit, or close a guided run after interruption. -**Develop flow** - exact reference name for the deploy, verify, and fix loop. +## Workflow -**Runtime target** - the app runtime the change targets, such as `appdev`, `appstage`, `app`, or a linked local target. +**Bootstrap** - workflow phase that reads the current project and settles which runtime services and managed services the app should use before app code work starts. -**Direct deploy** - a deploy from the current source to the scoped runtime through ZCP MCP. The first verified running result uses direct deploy before delivery setup is applied. +**Service setup** - the phase that decides which runtime services and managed services the app should use before app code work starts. -**Reachability verification** - checks that the runtime exists, is running, has no recent blocking errors, and can answer an HTTP probe when it is an HTTP service. +**Develop** - workflow phase that changes app code/config, deploys, verifies reachability and behavior, and fixes failures from evidence. -**Behavior verification** - checks that the user-requested app behavior works on the real URL, endpoint, worker result, or stored state. +**Runtime target** - the app runtime selected for the current change, such as `appdev`, `appstage`, `app`, or a linked local target. -**Completion evidence** - the runtime, deploy, reachability check, behavior check, URL/endpoint/UI/state proof, and delivery choice or blocker that justify calling the task done. +**Runtime layout** - which app runtime services the workflow should use: -**Proof** - user-inspectable completion evidence, such as a URL, endpoint result, UI state, processed job, or stored result. +- `standard` - dev runtime plus explicit stage runtime. +- `dev` - one mutable development runtime. +- `simple` - one runtime with no dev/stage split. +- `local-stage` - local source directory linked to one Zerops runtime as deploy target. +- `local-only` - local source directory with no linked runtime yet. + +**Managed service** - database, cache, queue, search, storage, mail, or similar dependency. It provides connection details; it is not an app deploy target. -**Workflow status** - a live project and session read the agent can use after interruption or confusion. Ask for it before letting the agent change anything else. +**Direct deploy** - deploy from the current source to the scoped runtime through ZCP MCP. The first verified running result uses direct deploy before delivery setup is applied. + +**Reachability verification** - checks that the runtime exists, is running, has no recent blocking errors, and can answer an HTTP probe when it is an HTTP service. + +**Behavior verification** - checks that the requested app behavior works on the real URL, endpoint, worker result, or stored state. + +**Proof** - user-inspectable completion evidence, such as a URL, endpoint result, UI state, processed job, or stored result. **Blocker** - a clear stop state with evidence: failure category, runtime in scope, what was tried, and what decision or credential is needed. -## Delivery and reuse +## Delivery and production -**Delivery choice** - what happens to future changes after a verified deploy: direct deploy, git-push, or external handoff. +**Delivery choice** - what happens after a verified deploy: keep direct deploy, push to git, or hand off to CI/release/human process. **Delivery mode** - exact reference label for delivery choice: @@ -90,46 +95,19 @@ Use the exact labels here when you are reading workflow status, writing agent po **Build integration** - repository-triggered build/deploy path that ZCP MCP may configure or observe, such as a Zerops dashboard webhook or GitHub Actions. It is separate from git-push capability and delivery mode. -**External handoff** - delivery mode where the workflow records proof and state, but a human or external system owns future deploys. - -**Package a running service** - handoff or reuse workflow that turns one deployed runtime and its managed dependencies into a re-importable bundle. It is not the normal way to deploy the next app change. +**Package a running service** - workflow that turns one verified runtime and its managed dependencies into a re-importable, git-backed Zerops bundle. -**Production promotion** - handoff from verified dev or stage work into a separate production Zerops project: production infrastructure setup, production deploy trigger setup, and repeated releases without putting ZCP in production. +**Production promotion** - handoff from verified dev or stage work into a separate production Zerops project through production infrastructure setup, production deploy trigger setup, and repeatable releases. -**Packaging env bucket** - exact labels used when classifying env vars during packaging: +**Production boundary** - policy that production should live in a separate Zerops project without a `zcp` service and receive promoted work through CI, release process, or human action. -- `infrastructure` - value comes from a managed-service reference and should be regenerated by the new project. -- `auto-secret` - local app secret that should be freshly generated on re-import. -- `external-secret` - third-party API key or credential that becomes a `REPLACE_ME` placeholder. -- `plain-config` - literal non-secret config copied into the bundle. - -## Project and runtime terms - -**Runtime service** - a service that runs app code. - -**Target runtime service** - the specific runtime service selected for the current app change. - -**Managed service** - database, cache, queue, search, storage, mail, or similar dependency. It provides connection details; it is not an app deploy target. - -**Runtime layout** - which app runtime services the workflow should use: - -- `standard` - dev runtime plus explicit stage runtime. -- `dev` - one mutable development runtime. -- `simple` - one runtime with no dev/stage split. -- `local-stage` - local source directory linked to one Zerops runtime as deploy target. -- `local-only` - local source directory with no linked runtime yet. - -**Dev runtime** - mutable runtime used for iterative development. - -**Stage runtime** - review or promotion target. Stage is not production. +## Platform and failure terms **Service scaling mode** - Zerops scaling setting such as `HA` or `NON_HA`. Different from runtime layout. -**Public subdomain access** - Zerops public URL access for an eligible HTTP runtime. Workers and non-HTTP services do not get useful public URLs. +**Public subdomain access** - Zerops `.zerops.app` URL access for an eligible HTTP runtime. Workers and non-HTTP services do not get useful public URLs. -## Failures and recovery - -**Failure category** - exact label that points to the first useful evidence surface: +**Failure category** - label that points to the first useful evidence surface: - `build` - build phase failed. - `start` - build passed, but runtime start or prepare failed. @@ -139,11 +117,9 @@ Use the exact labels here when you are reading workflow status, writing agent po - `credential` - Zerops, git, SSH, managed-service, or external API credential failed. - `other` - no known category matched. -**Recovery hint** - structured next move surfaced by ZCP MCP when the failure has an actionable recovery path. - **Confirmation gate** - an operation that pauses until the user explicitly confirms the named target or consequence. -**Destructive import override** - import action that would replace an existing service stack. ZCP MCP refuses first, names the affected services, and requires a matching acknowledgment before proceeding. +**Destructive import override** - import action that would replace an existing service stack. ZCP MCP refuses first, names affected services, and requires a matching acknowledgement before proceeding. ## Credentials @@ -155,4 +131,4 @@ Use the exact labels here when you are reading workflow status, writing agent po **`ZEROPS_TOKEN`** - Zerops token commonly used by GitHub Actions or external CI that runs `zcli`. -**External secret** - third-party credential such as Stripe, OpenAI, Mailgun, or GitHub API access. The agent can wire placeholders and env vars, but the secret itself remains your responsibility. +**External secret** - third-party credential such as Stripe, OpenAI, Mailgun, or GitHub API access. The agent can wire placeholders and env vars, but the secret value remains your responsibility. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index a8873a9da..1d085b382 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -87,7 +87,7 @@ Specific tasks and reference: | Decide how finished work ships | [Build and ship](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | | Understand token access and boundaries | [Trust model](/zcp/security/trust-model) | | Understand the feature concept | [Infrastructure for Coding Agents](/features/coding-agents) | -| Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | +| Understand workflow-guided runs | [Workflows in depth](/zcp/reference/agent-workflow) | | Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | ## What stays outside diff --git a/apps/docs/content/zcp/reference/agent-workflow.mdx b/apps/docs/content/zcp/reference/agent-workflow.mdx index 287dd8d79..2f3887262 100644 --- a/apps/docs/content/zcp/reference/agent-workflow.mdx +++ b/apps/docs/content/zcp/reference/agent-workflow.mdx @@ -1,69 +1,109 @@ --- -title: 'Workflow terms' -description: 'Precise ZCP MCP terms for setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence.' +title: 'Workflows in depth' +description: 'The bootstrap and develop process behind workflow-guided ZCP MCP agent runs.' --- -Exact vocabulary for ZCP MCP reference, audits, policies, and handoffs. Day-to-day prompts should describe outcomes. +Use this page when you want to understand the process behind workflow-guided agent work. Normal prompts should still describe outcomes. The workflow exists so the agent can read the project, prepare the right runtime and services, make the app change, deploy, verify, recover from evidence, and stop with proof or a concrete blocker. + +The useful mental split is: + +| Phase | What it settles | What should be true when it ends | +| ----- | --------------- | -------------------------------- | +| **Bootstrap** | Where the app should run and which services it depends on. | Runtime target and managed dependencies are known. | +| **Develop** | Code, `zerops.yaml`, env wiring, deploy, verification, recovery, and delivery choice. | The requested behavior is proved, or the blocker is concrete. | + +Bootstrap is not a marketing or onboarding step. It is the workflow's name for "make the project layout safe to work in before changing app code." ## Session layers Most workflow mistakes come from confusing these layers: -| Layer | What it is | What changes here | -| ---------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------- | -| ZCP MCP setup | Where the `zcp` binary runs: remote `zcp@1` service or local machine. | No app code should be deployed to the setup itself. | -| Target runtime service | The app runtime: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys. | -| Managed services | Databases, caches, queues, search, storage, mail. | Schema/data operations and credentials, never app code deploys. | +| Layer | What it is | What changes here | +| ----- | ---------- | ----------------- | +| **Workspace** | Where `zcp` and the agent run: remote `zcp@1` service or local machine. | Agent config, tools, local workflow state. App code should not be deployed to the workspace itself. | +| **Target runtime service** | The app runtime in scope: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys, runtime logs, verification target. | +| **Managed services** | Databases, caches, queues, search, storage, mail, and similar dependencies. | Schema/data operations and credentials, never app code deploys. | The `zcp` service is the control surface, not the app runtime. -## Service setup routes +## What drives the workflow + +The workflow-guided experience is made from four pieces: + +| Piece | Role | +| ----- | ---- | +| **MCP tools** | Project-scoped Zerops operations: discover, deploy, read logs/events, manage env vars, verify, import/export, and related actions. | +| **Generated instructions** | Agent policy for when to inspect, when to ask, when to deploy, what evidence to read, and what counts as done. | +| **Saved workflow state** | Local metadata about bootstrap sessions, runtime pairing, delivery choice, deploy attempts, verify attempts, and interrupted work. | +| **Live Zerops project** | Source of truth for services, status, env refs, logs, events, deploys, runtime files, and public access. | + +The workflow does not replace judgment from the user or the agent. It gives the agent a process and evidence surface so app work does not depend on stale chat memory or a pasted runbook. -Before app code work starts, ZCP reads live state and chooses a setup route. The route is an implementation detail in first-read docs, but it is useful in reference and audits. +## Bootstrap + +Bootstrap starts before app code changes when the workflow needs to understand or prepare the project layout. ```mermaid flowchart TD - start(["Read project state"]) - mid{"Interrupted setup
to resume?"} - runtimes{"Runtime services
already exist?"} - known{"Request matches
known recipe/stack?"} + start(["Read live project state"]) + interrupted{"Interrupted bootstrap
to resume?"} + runtime{"Runtime services
already exist?"} + known{"Request matches
known recipe or stack?"} resume(["resume"]) adopt(["adopt"]) recipe(["recipe"]) classic(["classic"]) - start --> mid - mid -- yes --> resume - mid -- no --> runtimes - runtimes -- yes --> adopt - runtimes -- no --> known - known -- yes --> recipe - known -- no --> classic + done(["Runtime target and dependencies known"]) + + start --> interrupted + interrupted -- yes --> resume --> done + interrupted -- no --> runtime + runtime -- yes --> adopt --> done + runtime -- no --> known + known -- yes --> recipe --> done + known -- no --> classic --> done ``` -| Route | Use when | Wrong signal | -| --------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -| `adopt` | Runtime services already exist, including recipe-created projects. | Recreating services that exist, or targeting `zcp` as the app. | -| `recipe` | The project is empty or only has ZCP, and the request matches a known stack recipe. | Deploying an unchanged starter as if it were the requested product. | -| `classic` | The project is empty and needs a custom service plan. | Writing app code before service ownership and runtime target are known. | -| `resume` | A previous setup was interrupted. | Starting from scratch without acknowledging live services and saved state. | +| Route | Use when | Wrong signal | +| ----- | -------- | ------------ | +| `adopt` | Runtime services already exist, including recipe-created projects. | Recreating services that already fit, or targeting the `zcp` service as the app. | +| `recipe` | The project is empty or only has remote setup, and the request matches a known stack recipe. | Treating an unchanged starter as the finished requested product. | +| `classic` | The project is empty and needs a custom service plan. | Writing app code before service ownership and runtime target are known. | +| `resume` | A previous bootstrap was interrupted. | Starting from scratch without checking live services and saved state. | + +Bootstrap ends when the app runtime target and managed dependencies are known. It should also make visible any choice that needs human judgment: cost, credentials, data, runtime layout, production risk, or destructive behavior. + +## Runtime layouts + +Runtime layout describes which app runtime services the workflow should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. + +| Layout | Meaning | Typical names | +| ------ | ------- | ------------- | +| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage is explicit. | `appdev` + `appstage` | +| `dev` | One mutable development runtime. | `appdev` | +| `simple` | One runtime with no dev/stage split. | `app` | +| `local-stage` | Local source directory linked to one Zerops runtime for deploys. | local directory + `appstage` | +| `local-only` | Local source directory with no linked deploy target yet. | local directory | -Service setup stops when app runtimes and managed dependencies are known. App code, `zerops.yaml`, and the first deploy belong to the develop flow. +In `standard`, stage is explicit. Work scoped to `appdev` does not silently touch `appstage`; promotion or stage verification happens when the user asks for it. -## Develop flow +## Develop -The develop loop is the main workflow cycle. It closes only when reachability and requested behavior both pass. +Develop is the main app-work loop. It begins after bootstrap has a runtime target and dependencies. It closes only when runtime reachability and requested behavior both pass, or when the agent has a blocker that needs a human decision. ```mermaid flowchart TD scope["1. Name runtime target"] change["2. Change code/config"] - deploy["3. Deploy in-scope runtime"] - reach{"4. Runtime reachability
passes?"} - behavior{"5. Requested behavior
passes?"} - done(["Done: proof or URL"]) - fix["6. Categorize failure,
read evidence, fix"] + deploy["3. Direct deploy"] + serve["4. Start or restart if needed"] + reach{"5. Runtime reachable?"} + behavior{"6. Requested behavior works?"} + done(["Proof"]) + fix["7. Categorize failure
read evidence, fix"] blocker(["Blocker"]) - scope --> change --> deploy --> reach + + scope --> change --> deploy --> serve --> reach reach -- yes --> behavior reach -- no --> fix behavior -- yes --> done @@ -72,90 +112,102 @@ flowchart TD fix --> blocker ``` -1. **Name runtime target.** State which runtime is in scope (`appdev`, `appstage`, `app`, or linked local target). In dev+stage projects, dev work does not imply stage unless requested. -2. **Change code/config.** Edit app files, `zerops.yaml`, env references, migrations, seeds, or framework config. -3. **Deploy directly first.** The first verified runtime deploy goes through ZCP directly. Delivery setup is applied after a verified running result exists. -4. **Verify runtime reachability.** Service status, recent error logs, and HTTP probe for eligible runtime services. -5. **Verify requested behavior.** Endpoint body, UI state, job result, persisted data, or another check tied to the user request. -6. **Fix from evidence.** Read classification, logs, events, and check output. Repeating the same deploy without new evidence is not progress. +1. **Name runtime target.** State which runtime is in scope: `appdev`, `appstage`, `app`, or a linked local target. +2. **Change code and config.** Edit app files, `zerops.yaml`, env references, migrations, seeds, framework config, or local `.env` bridge when needed. +3. **Deploy directly first.** The first verified runtime deploy goes through MCP tools. Git or CI handoff comes after proof. +4. **Start or restart if needed.** Dynamic dev runtimes may need an explicit start or restart after deploy. Built-in webserver runtimes do not need a separate dev-server step unless the framework requires one. +5. **Verify runtime reachability.** Check service status, recent error logs, and HTTP readiness when the runtime is an HTTP service. +6. **Verify requested behavior.** Check endpoint body, UI state, job result, persisted data, or another result tied to the user request. +7. **Fix from evidence.** Read failure category, logs, events, and check output. Repeating the same deploy without new evidence is not progress. -Dynamic dev runtimes may need an explicit start or restart after deploy. Built-in webserver runtimes do not need a separate dev-server step unless the framework requires one. +Reachability and requested behavior are separate gates. A green deploy with a broken route is not done. -## Runtime layouts +## Failure categories -Runtime layout describes which app runtime services ZCP should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. +Failure categories point the agent to the first useful evidence surface. -| Layout | Meaning | Typical names | -| ------------- | ---------------------------------------------------------------------------------------------- | ---------------------------- | -| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage is explicit. | `appdev` + `appstage` | -| `dev` | One mutable development runtime. | `appdev` | -| `simple` | One runtime with no dev/stage split. | `app` | -| `local-stage` | Local source directory linked to one Zerops runtime for deploys. | local directory + `appstage` | -| `local-only` | Local source directory with no linked deploy target yet. | local directory | +| Category | What it means | Read first | +| -------- | ------------- | ---------- | +| `build` | Build phase failed. | Build logs, build commands, dependency manifests, deploy file list. | +| `start` | Build passed, but runtime start or prepare failed. | Prepare/runtime logs, start command, ports, env references. | +| `verify` | Runtime exists, but reachability or behavior failed. | Failing check detail, HTTP response, request-time runtime logs, behavior evidence. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | VPN, SSH, DNS, subdomain readiness, service status, named transport error. | +| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Field-level rejection, setup name, env reference, service settings. | +| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | +| `other` | No known category matched. | Raw event/log evidence; stop if repeated without a new signal. | -The stage hostname comes from live state. ZCP should not invent it from the dev hostname. +Categorization is what turns retries into evidence-driven fixes. The practical recovery flow lives in [Troubleshooting](/zcp/reference/troubleshooting). -## Delivery modes +## Delivery after proof -Delivery mode applies after a verified deploy and describes how future changes ship. It does not redirect the first successful deploy. +Delivery mode applies after a verified deploy. It does not replace the first proof. -| Exact mode | User-facing choice | Meaning | -| ---------- | ------------------ | ------------------------------------------------------------------------------------------------------------------ | -| `auto` | Keep direct deploy | ZCP keeps deploying future changes directly to the target runtime. | -| `git-push` | Push to git | The agent commits and pushes to a configured remote; any resulting build still needs observation and verification. | -| `manual` | External handoff | CI, release process, or a human owns future delivery; the workflow records evidence but does not initiate the next deploy. | +| Exact mode | User-facing choice | Meaning | +| ---------- | ------------------ | ------- | +| `auto` | Keep direct deploy | The agent keeps deploying future changes directly to the target runtime. | +| `git-push` | Push to git | The agent commits and pushes to a configured remote; any resulting build still needs observation and verification. | +| `manual` | External handoff | CI, release process, or a human owns future delivery; the workflow records evidence but does not initiate the next deploy. | Git-push capability, delivery mode, and build integration are separate. A project can have git-push configured while still using direct deploy for a given session. -## Failure categories +Packaging and production promotion are deliberate handoffs after proof. Packaging turns a verified runtime into a git-backed import bundle. Production promotion moves verified work into a separate production project through GUI setup, git/CI triggers, or the team's release process. + +## Generated files and state + +Remote setup and `zcp init` create configuration around the MCP server and workflow guidance. -| Category | Meaning | Read first | -| ------------ | --------------------------------------------------------------------- | ------------------------------------------------------ | -| `build` | Build phase failed. | Build logs and build commands. | -| `start` | Build passed, runtime failed to start or crashed. | Prepare/runtime logs and start command. | -| `verify` | Runtime exists but reachability or behavior failed. | Failing check detail and runtime logs at request time. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and failed network access. | -| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Structured rejection detail and config files. | -| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | -| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +| File or directory | Created where | Purpose | +| ----------------- | ------------- | ------- | +| `CLAUDE.md` | Remote workspace or local project directory | Claude Code instruction surface. ZCP MCP writes a managed block between `` and ``. User content outside that block is preserved. | +| `.claude/settings.local.json` | Remote workspace or local project directory | Claude Code per-project settings and ZCP MCP tool permissions for that directory. | +| `.mcp.json` | Local project directory | Project-local MCP server config. It contains the local `zcp` command and the project-scoped `ZCP_API_KEY`; keep it out of git. | +| `~/.claude.json` and SSH config | Remote setup | Workspace-level Claude Code and SSH wiring used inside the Zerops-hosted workspace. | +| `.zcp/state/` | Working directory where the MCP server runs | Workflow state and service metadata for that project directory. | -Categorization is what turns retries into evidence-driven fixes. Recovery moves and symptom mapping live in [Troubleshooting](/zcp/reference/troubleshooting). +The managed `CLAUDE.md` block is refreshed by `zcp init` and by the MCP server when the block already exists. Put durable project instructions outside the ZCP markers. Edits inside the managed block are treated as generated content. + +`.zcp/state/` is not application source code and should not be committed. It stores metadata such as known runtime services, local/stage pairing, delivery preference, git-push setup, build integration, first-deploy stamps, workflow sessions, deploy attempts, verify attempts, and local coordination locks. + +ZCP MCP does not use `.zcp/state/` as a stale copy of the Zerops project. Service status, logs, events, runtime files, env-var values, and current platform configuration are read from Zerops or from the local filesystem when tools run. Local `.env` files are generated separately and may contain secrets. + +Do not edit `.zcp/state/` by hand during normal work. Use workflow operations to reset, resume, iterate, or reconfigure state. Deleting it intentionally discards local memory of services and delivery setup for that directory; the tools can rediscover live Zerops state, but delivery preferences and workflow history may need to be set again. ## Completion evidence -A completed app task can answer: +A completed app task should answer: -- which services were used or created, +- which bootstrap route was used when setup was needed, - which runtime target changed, +- which managed services were used or created, - which deploy passed, - which reachability check passed, -- which user-requested behavior passed, -- which URL, endpoint, UI state, or stored result proves it, -- which delivery mode applies next, +- which requested behavior passed, +- which URL, endpoint, UI state, worker result, or stored value proves it, +- which delivery choice applies next, - or which blocker remains and what evidence supports it. -A green build with a broken route is not completion. A clear blocker is acceptable completion only when it names the failure category, evidence read, fixes tried, and human decision or credential still needed. +A clear blocker is acceptable completion only when it names the runtime in scope, failure category, evidence read, fixes tried, and human decision or credential still needed. + +## Confirmation gates + +Some operations pause because the loss is not safely reversible from inside the conversation: -## Practical rules +- **Service deletion** requires explicit user approval in the current conversation, by service name. +- **Destructive import override** first refuses and names what would be replaced; a second call must acknowledge the same targets. -- Use existing services when they fit; do not recreate them just to get a clean slate. -- Bootstrap/service setup is not app work; app files and first deploy happen in develop. -- First functional deploy is direct; git/CI/handoff comes after proof. -- Managed services are dependencies, not deploy targets. -- Stage is explicit in `standard` projects; dev changes do not silently promote. -- Runtime health and requested behavior are separate verification gates. -- Stop on repeated failure without new evidence, missing credentials, ambiguous runtime target, or destructive recovery. +See [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) for the user-facing confirmation flow. -## Auditing a session +## Auditing a workflow -A session is well-shaped if platform evidence and the agent report answer: +A workflow-guided run is well-shaped if the evidence answers: -| Question | Evidence | -| ----------------------------------------------- | ------------------------------------------------------------------------- | -| Which setup route and runtime layout were used? | Workflow status, service metadata, service list. | -| Where did service setup end and app work begin? | Work-session state, deploy history, file changes. | -| Which runtime was deployed and verified? | Service events, deploy result, verify output. | -| What behavior proved success? | Endpoint/UI/job/data proof in the final report. | -| What controls future delivery? | Delivery mode, git-push state, build integration, or manual handoff note. | +| Question | Evidence | +| -------- | -------- | +| Where did bootstrap start? | Live service list, saved workflow state, and bootstrap route. | +| Where did bootstrap end? | Runtime target, managed dependencies, and any human decisions. | +| Which runtime was developed and deployed? | Runtime target, deploy result, events, and logs. | +| What proved reachability? | Verify output, service status, public URL, or HTTP probe. | +| What proved behavior? | Endpoint, UI flow, job result, database/object state, or other requested proof. | +| What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production handoff note. | -Operation-name reference lives in [ZCP MCP tools](/zcp/reference/mcp-operations). Recovery shortcuts live in [Troubleshooting](/zcp/reference/troubleshooting). +For operation names and direct tool calls, use [ZCP MCP tools](/zcp/reference/mcp-operations). For exact terms, use the [Glossary](/zcp/glossary). diff --git a/apps/docs/content/zcp/reference/index.mdx b/apps/docs/content/zcp/reference/index.mdx index 01224767a..aa43a11b7 100644 --- a/apps/docs/content/zcp/reference/index.mdx +++ b/apps/docs/content/zcp/reference/index.mdx @@ -1,13 +1,13 @@ --- title: "Reference" -description: "Precise ZCP MCP terms, operation names, troubleshooting, and glossary." +description: "Process details, MCP tool names, recovery guidance, and glossary for ZCP MCP." --- Use reference pages when exact labels matter. Day-to-day app work starts in [Build and ship](/zcp/workflows/build-with-zcp). | Need | Page | |---|---| -| Name setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence | [Workflow terms](/zcp/reference/agent-workflow) | -| Use ZCP MCP tools directly, understand generated `.zcp`/Claude files, or look up operation names | [ZCP MCP tools](/zcp/reference/mcp-operations) | +| Understand what sits behind workflow-guided agent runs: generated instructions, state, process gates, and completion evidence | [Workflows in depth](/zcp/reference/agent-workflow) | +| Use ZCP MCP tools directly or look up operation names for a custom MCP integration | [ZCP MCP tools](/zcp/reference/mcp-operations) | | Recover a stuck or confused session | [Troubleshooting](/zcp/reference/troubleshooting) | | Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | diff --git a/apps/docs/content/zcp/reference/mcp-operations.mdx b/apps/docs/content/zcp/reference/mcp-operations.mdx index dcebda5c0..a0977c879 100644 --- a/apps/docs/content/zcp/reference/mcp-operations.mdx +++ b/apps/docs/content/zcp/reference/mcp-operations.mdx @@ -1,107 +1,91 @@ --- title: 'ZCP MCP tools' -description: 'Use ZCP MCP tools directly, understand generated state, and look up common operation names.' +description: 'Use ZCP MCP as a direct MCP tool surface: what the tools do, what they do not decide, and which operations exist.' --- -Most users never need operation names. They matter when you configure agent-client policy, debug an MCP integration, or automate ZCP MCP directly. For normal app work, ask the agent for outcomes. Recipe-authoring and ZCP-internal maintenance operations are intentionally out of scope. +Most app work should be phrased as an outcome: build this, fix that, deploy and prove it. Operation names matter when you configure agent-client policy, debug an MCP integration, build a custom client, or use ZCP MCP pragmatically as a set of project-scoped Zerops tools. -## Direct MCP calls +ZCP MCP is the MCP server exposed by the `zcp` binary. Any MCP-capable client can connect to it and call tools such as `zerops_discover`, `zerops_logs`, `zerops_events`, `zerops_deploy`, or `zerops_verify`. -ZCP MCP is the MCP server exposed by the `zcp` binary. Workflow instructions are not required for every integration. - -Any MCP-capable client can connect to ZCP MCP and call individual tools such as `zerops_discover`, `zerops_logs`, `zerops_events`, `zerops_deploy`, or `zerops_verify`. This is useful for custom agent clients, scripts, dashboards, policy-gated clients, or debugging sessions where you want one Zerops operation instead of a full workflow-guided task. - -The higher-level behavior comes from generated agent instructions, especially `CLAUDE.md`. Those instructions tell Claude Code when to call `zerops_workflow`, how to discover services, how to treat deploy and verify evidence, and when a task is done. The MCP server exposes the tools; the generated instructions teach the agent the lifecycle. Nothing requires another MCP client to call `zerops_workflow` unless you add equivalent policy yourself. - -Using only the tools is valid. In that setup, your integration owns the sequencing: which services are in scope, when to deploy, what counts as verification, how to read logs or events, and when to stop. Tool-level gates still apply, including explicit service deletion approval and destructive import override acknowledgement. - -## Generated files - -`zcp init` and remote setup create a small amount of agent configuration around the MCP server. - -| File or directory | Created where | Purpose | -| ----------------- | ------------- | ------- | -| `CLAUDE.md` | Remote workspace or local project directory | Claude Code instruction surface. ZCP MCP writes a managed block between `` and ``. User content outside that block is preserved. | -| `.claude/settings.local.json` | Remote workspace or local project directory | Claude Code project settings and ZCP MCP tool permissions for that directory. | -| `.mcp.json` | Local project directory | Project-local MCP server config. It contains the local `zcp` command and the project-scoped `ZCP_API_KEY`; keep it out of git. | -| `~/.claude.json` and SSH config | Remote setup | Workspace-level Claude Code and SSH wiring used inside the Zerops-hosted workspace. | -| `.zcp/state/` | Working directory where the MCP server runs | Workflow state and service metadata for that project directory. | - -The managed `CLAUDE.md` block is refreshed by `zcp init` and by the MCP server when the block already exists. Put durable project instructions outside the ZCP markers. Edits inside the managed block are treated as generated content. - -## `.zcp/state` +## Tool-only use -`.zcp/state/` is project-scoped state for workflow-aware ZCP MCP use. It is not application source code and should not be committed. +The MCP server exposes operations. It does not, by itself, decide the whole app lifecycle. -| Path | What it stores | -| ---- | -------------- | -| `.zcp/state/services/*.json` | Persistent service metadata: known runtime services, local/stage pairing, delivery preference, git-push setup, build integration, and first-deploy stamps. | -| `.zcp/state/sessions/*.json` | Bootstrap, recipe, or packaging workflow sessions. | -| `.zcp/state/work/{pid}.json` | The current agent-process work session: task intent, services in scope, recent deploy attempts, recent verify attempts, and close status. | -| `.zcp/state/registry.json` | Session ownership and lookup data for active workflow state. | -| `.zcp/state/locks/` | Local coordination locks for stateful operations. | +Workflow-guided Claude Code uses generated instructions to decide sequencing: inspect state before changing things, choose the runtime target, use existing services or create missing ones, deploy, read failure evidence, verify requested behavior, and stop with proof or a blocker. -ZCP MCP does not use `.zcp/state/` as a stale copy of the Zerops project. Service status, logs, events, runtime files, env-var values, and current platform configuration are read from Zerops or from the local filesystem when tools run. Local `.env` files are generated separately and may contain secrets; `.zcp/state/` is metadata for workflow coordination. +Tool-only use is valid when you want a custom MCP client, script, dashboard, policy-gated agent, or narrow operational task. In that setup, your integration owns the sequencing: -Do not edit `.zcp/state/` by hand during normal work. Use ZCP MCP tools to reset, resume, iterate, or reconfigure workflow state. Deleting it intentionally discards local memory of services and delivery setup for that directory; the tools can rediscover live Zerops state, but delivery preferences and workflow history may need to be configured again. +- which project services are in scope, +- when a deploy is allowed, +- what counts as verification, +- which logs or events should be read after failure, +- when to ask a human, +- when to stop. -## Tool-only use +Tool-level gates still apply. Service deletion requires explicit named approval, and destructive import override requires an acknowledgement of the exact targets. -If you do not want the generated workflow to guide Claude Code, keep the MCP connection and remove the ZCP-managed workflow block from `CLAUDE.md`, or keep your own policy outside the ZCP markers. You can also use a different MCP client that never reads that file. +If you use Claude Code but do not want the generated workflow guidance, keep the MCP connection and remove the ZCP-managed workflow block from `CLAUDE.md`, or keep your own policy outside the ZCP markers. What you lose is the generated instruction layer that makes the agent plan around live state, deploy with bounded retries, verify behavior, and report proof or a concrete blocker. -After that, direct MCP operations still work. The client can call `zerops_discover`, `zerops_logs`, `zerops_events`, `zerops_env`, `zerops_deploy`, `zerops_verify`, and the other tools directly. What you lose is the generated instruction layer that makes the agent plan around live state, use workflow status, deploy with bounded retries, verify behavior, and report proof or a concrete blocker. +Generated files and workflow state are documented in [Workflows in depth](/zcp/reference/agent-workflow#generated-files-and-state). -This is a reasonable advanced setup when you are building your own integration or want narrow operational access. Be explicit about the policy you expect from the agent, and test the integration more carefully because the generated workflow is no longer steering the whole task lifecycle for you. +## What the tools can do -## Permission classes +| Area | Tools | Typical use | +| ---- | ----- | ----------- | +| Discover | `zerops_discover` | Read services, service metadata, env-var keys, and project topology. | +| Observe | `zerops_logs`, `zerops_events`, `zerops_verify`, `zerops_process` | Diagnose deploys, read runtime/build evidence, run health checks, and watch async processes. | +| Deploy | `zerops_deploy` | Push source through Zerops build/deploy pipeline and return build/deploy evidence. | +| Configure | `zerops_env`, `zerops_subdomain`, `zerops_scale`, `zerops_manage` | Change env vars, public subdomain access, scaling, lifecycle, reload/restart, and storage attachment. | +| Import/export | `zerops_import`, `zerops_export`, `zerops_preprocess` | Import project/service YAML, read export YAML, and expand Zerops preprocessor expressions. | +| Workspace | `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | +| Workflow | `zerops_workflow` | Track, recover, or configure workflow sessions and packaging/export flow. | +| Destructive | `zerops_delete` | Delete one named service after explicit user approval. | -| Class | Meaning | -| ----------------- | ----------------------------------------------------------------------------------------- | -| Read-only | Inspect state, logs, events, guidance, or verification output. | -| Mutating | Change services, env vars, scaling, deploy state, or lifecycle. | -| Operational setup | Coordinate workflows, mounts, imports, recipes, delivery setup, or generated local files. | +Recipe-authoring and ZCP-internal maintenance operations are intentionally out of scope for this public reference. ## Read-only operations -| Operation | Purpose | -| ------------------ | ----------------------------------------------------------------------------- | -| `zerops_discover` | Read services, ports, env-var keys, and current state. | -| `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | -| `zerops_events` | Read service activity, deploys, builds, scaling, and failures. | -| `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | -| `zerops_knowledge` | Fetch ZCP MCP guidance or platform knowledge for the current state. | -| `zerops_process` | Check a known async process; cancel is a mutating action. | +| Operation | Purpose | +| --------- | ------- | +| `zerops_discover` | Read services, ports, env-var keys, and current state. | +| `zerops_export` | Read platform project/service export YAML and service metadata. | +| `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | +| `zerops_events` | Read service activity, deploys, builds, scaling, and failures. | +| `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | +| `zerops_knowledge` | Fetch ZCP MCP guidance or platform knowledge for the current state. | +| `zerops_process` | Check a known async process; cancel is a mutating action. | ## Mutating operations -| Operation | Purpose | -| ------------------ | -------------------------------------------------------------------------------------------------------------------- | -| `zerops_deploy` | Ship code through the Zerops build and deploy pipeline. | -| `zerops_env` | Read, set, delete, or generate env vars and local `.env` files. | -| `zerops_manage` | Start, stop, restart, reload, or connect storage. | -| `zerops_scale` | Change CPU, RAM, disk, CPU mode, or container autoscaling where supported. `HA`/`NON_HA` is set at service creation. | -| `zerops_subdomain` | Enable or disable public subdomain access. | -| `zerops_delete` | Delete a service. Explicit named approval is required. | +| Operation | Purpose | +| --------- | ------- | +| `zerops_deploy` | Ship code through the Zerops build and deploy pipeline. | +| `zerops_env` | Read, set, delete, or generate env vars and local `.env` files. | +| `zerops_manage` | Start, stop, restart, reload, or connect storage. | +| `zerops_scale` | Change CPU, RAM, disk, CPU mode, or container autoscaling where supported. `HA`/`NON_HA` is set at service creation. | +| `zerops_subdomain` | Enable or disable public subdomain access. | +| `zerops_delete` | Delete a service. Explicit named approval is required. | ## Operational setup -| Operation | Purpose | -| ------------------- | ----------------------------------------------------------------------------------------------------- | -| `zerops_workflow` | Track, recover, or configure workflow sessions; also carries the package-running-service export flow. | -| `zerops_import` | Import project/service definitions. Destructive override is gated. | -| `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | -| `zerops_preprocess` | Expand Zerops preprocessor expressions. | -| `zerops_export` | Read platform project/service export YAML and service metadata. | - -## Remote and local differences - -| Area | Remote setup | Local setup | -| ------------- | ------------------------------------------------ | ------------------------------------------------------------- | -| Files | Runtime files through SSHFS or remote containers | Local working directory | -| Deploy source | Remote service or batch deploy inside Zerops | Local `workingDir` | -| Dev server | Remote setup can run or inspect remote dev processes | Your local tool owns the dev server | -| Env bridge | Env vars are already available in Zerops | `.env` generation resolves Zerops references for local use | -| Git | Workspace-managed credentials | User's local git credentials | +| Operation | Purpose | +| --------- | ------- | +| `zerops_workflow` | Track, recover, or configure workflow sessions; also carries the package-running-service export flow. | +| `zerops_import` | Import project/service definitions. Destructive override is gated. | +| `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | +| `zerops_preprocess` | Expand Zerops preprocessor expressions. | + +## Remote and local tool differences + +The operation names are the same, but the filesystem and network boundary differ. + +| Area | Remote setup | Local setup | +| ---- | ------------ | ----------- | +| Files | Runtime files through SSHFS or remote containers. | Local working directory. | +| Deploy source | Remote service or batch deploy inside Zerops. | Local `workingDir`. | +| Dev server | Remote setup can run or inspect remote dev processes. | Your local tool owns the dev server. | +| Env bridge | Env vars are already available in Zerops. | `.env` generation resolves Zerops references for local use. | +| Git | Workspace-managed credentials. | User's local git credentials. | ## Confirmation gates diff --git a/apps/docs/content/zcp/reference/troubleshooting.mdx b/apps/docs/content/zcp/reference/troubleshooting.mdx index 97d22859a..dcbf3faa0 100644 --- a/apps/docs/content/zcp/reference/troubleshooting.mdx +++ b/apps/docs/content/zcp/reference/troubleshooting.mdx @@ -1,120 +1,101 @@ --- title: 'Troubleshooting' -description: 'Recover ZCP sessions from current state, failure category, logs, events, tokens, VPN, and local setup artifacts.' +description: 'A recovery playbook for stuck ZCP MCP sessions: status first, evidence order, local setup checks, and stop conditions.' --- -Start recovery from current state, not from chat memory. +Use troubleshooting when the agent is confused, a session was interrupted, deploy keeps failing, verification does not match the final answer, or you are taking over manually. + +Start from current state, not chat memory: ```text Read current project status and tell me where things stand before changing anything. ``` -That prompt fits confused sessions, interrupted browser tabs or CLI sessions, repeated deploy loops, and manual takeovers. The agent can rebuild the picture from live services, recent deploys, logs, events, and saved work state. - -## Start from the failure category - -When a deploy or verify step fails, ZCP should surface a category with the likely cause and next move. Categories are recovery-shaped. `network` does not mean "the network is broken"; it means the first useful move is connectivity, VPN, SSH, DNS, or public-route evidence rather than editing app code. +That should make the agent read live services, saved workflow state, recent deploys, logs, events, and verification output before it edits anything else. -| Category | Meaning | Read first | Recovery move | -| ------------ | --------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| `build` | Build failed before runtime start. | Build logs and `zerops.yaml` build steps. | Fix build commands, dependency manifests, lock files, deploy files, or build resources. Runtime logs will not explain this failure. | -| `start` | Build passed, runtime failed to start or crashed. | Prepare logs or runtime logs, whichever the failure names. | Check `run.start`, ports, env references, `prepareCommands`, and whether the process binds `0.0.0.0` rather than `127.0.0.1`. | -| `verify` | Runtime exists, but reachability or requested behavior failed. | The failing check, HTTP response, and runtime logs at request time. | Treat it as app behavior or route recovery, not deploy success. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | The transport error and named network route. | Check VPN for local setup, service status for remote setup, SSH transfer state, DNS/subdomain readiness, or platform timeout. | -| `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Structured rejection detail, field path, or API metadata. | Fix the named setup block, env reference, `deployFiles`, service type, or prerequisite. | -| `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | The credential surface named in the error. | Replace that credential only: `ZCP_API_KEY`, `GIT_TOKEN`, local git auth, `ZEROPS_TOKEN`, SSH key, or app secret. | -| `other` | No known category matched. | Raw reason, service events, and logs for the named phase. | Stop if the same unknown reason repeats; report the raw reason and recent evidence. | +## Find where the run is stuck -Build logs and runtime logs are different streams. A build failure shows in the build container; a runtime crash shows in runtime logs. If the agent reads the wrong stream, the diagnosis will be wrong even when the log command succeeded. - -## Deploy and start symptoms - -| Symptom | Likely category | Next move | -| --------------------------------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------- | -| Build exits with "command not found", missing module, npm 404, or compile error | `build` | Read `buildCommands`, manifests, lock files, package manager, and deploy file list. | -| Build or SSH transfer ends with `signal: killed` | `build` / `network` | Treat it as memory pressure first: scale build/source resources or reduce the heavy step. | -| Runtime crashes immediately with `EADDRINUSE`, missing module, or missing env var | `start` | Read runtime logs scoped to the failed start and check the start command/env contract. | -| Database connection refused during init/start | `start` / `network` | Confirm the managed service is running and env references such as `${db_*}` resolve. | -| Preflight refuses `INVALID_ZEROPS_YML` or setup mismatch | `config` | Read the field-level rejection and fix that setup entry; setup names are not always hostnames. | -| Git push returns authentication failed | `credential` | Remote setup needs `GIT_TOKEN`; local setup uses your local git credentials. | +| Stuck point | Ask for | What you should get | +| ----------- | ------- | ------------------- | +| Agent lost context | Current project status and services in scope. | Runtime target, managed services, last deploy, last verify result, and any saved workflow state. | +| Service setup is unclear | Runtime target and dependency plan. | Which existing services will be used, which missing services are needed, and what needs human approval. | +| Deploy failed | Failure category plus build logs, runtime logs, and recent service events. | A cause or next diagnostic step, not another blind deploy. | +| Runtime is reachable but app behavior fails | The failing behavior check and request-time runtime logs. | Endpoint/UI/job/data evidence tied to the product request. | +| Local app cannot reach services | VPN state, generated `.env`, and selected runtime/setup. | Whether MCP auth works separately from private service access. | +| Delivery is ambiguous | Delivery mode, git-push state, build integration, or handoff note. | What will happen after proof: direct deploy, git push, CI, package, or production handoff. | Useful prompt: ```text -Show me the failure category, build logs, runtime logs, and recent events for the last failed deploy of appdev. +Show me the runtime in scope, failure category, evidence read, fixes tried, and the next decision needed. ``` -## Verify and public URL symptoms +## Evidence order -A deploy can be green and still fail verification. Verify has two layers: platform reachability, then the requested behavior. +The useful evidence depends on the failure category surfaced by deploy or verify tools. -| Symptom | Likely cause | Next move | -| ------------------------------------------------------------ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| Service is `RUNNING`, but the route returns 500 or 404 | App route, dependency, or request handling is broken. | Read runtime logs at request time and verify the requested route/body, not only `/`. | -| Public URL returns connection error right after first deploy | Subdomain route is still propagating, or the runtime is not bound correctly. | Wait 30-60 seconds and retry; if it persists, check port binding and public-access eligibility. | -| 502 persists after deploy and wait | Start command binds `127.0.0.1`, wrong port, or HTTP server not listening. | Check `zerops.yaml` `run.ports[]` and make the app bind the same port on `0.0.0.0`. | -| Subdomain stays disabled | Worker or non-HTTP service, or explicit public access missing. | Workers do not get useful public URLs. If it is an HTTP runtime, ask the agent to enable subdomain access and verify it. | -| Requested feature fails after a successful deploy | Behavior verification failed. | Continue the loop from logs/events/check output; do not report the task done. | +| Category | Read first | Avoid | +| -------- | ---------- | ----- | +| `build` | Build logs, build commands, dependency manifests, deploy file list. | Runtime logs; the runtime did not start yet. | +| `start` | Prepare/runtime logs, start command, ports, env references. | Rebuilding without checking why the process exited. | +| `verify` | Failing check detail, HTTP response, request-time runtime logs, stored state. | Calling a green deploy "done" before behavior passes. | +| `network` | VPN, SSH, DNS, subdomain readiness, service status, transport error. | Editing app code before proving connectivity. | +| `config` | Field-level rejection, `zerops.yaml`, setup name, env references, service settings. | Guessing from service names or dashboard memory. | +| `credential` | The named credential surface: `ZCP_API_KEY`, git, SSH, managed-service, CI, or external API. | Rotating unrelated secrets. | +| `other` | Raw events/logs and the exact phase that failed. | Repeating the same attempt after the same unknown reason. | -Public subdomain access is enabled on first deploy for eligible HTTP runtimes. Workers and non-HTTP services do not get a useful public URL by design. +Failure categories come from the deploy/verify evidence surface. They are not a verdict; they tell the agent where the next useful signal is. -## Token and credential symptoms +## Local setup checks -Token problems usually happen at startup. The fix is to replace the token on the surface ZCP reads, then restart the agent so the new process inherits it. +Local setup has two separate connections: -| Symptom | Likely cause | Next move | -| -------------------------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| `Token accesses N projects; use project-scoped token` | Multi-project or account-wide token. | Generate a single-project token and replace `ZCP_API_KEY`. | -| `Token has no project access` | Token authenticates but reaches no project. | Grant project access or generate a new single-project token. | -| `No authentication found: set ZCP_API_KEY or log in with zcli` | No token reached ZCP. | Add `ZCP_API_KEY` under `.mcp.json` `env` for local setup, or check remote service env. | -| API replies with 401 or `AUTH_TOKEN_EXPIRED` | Token expired or was revoked. | Generate a new single-project token, replace it, restart the agent. | -| GitHub Actions deploy fails on auth | `ZEROPS_TOKEN` missing/wrong. | Set a Zerops API token as the repository secret named `ZEROPS_TOKEN`. It is not a GitHub token. | +- MCP uses `ZCP_API_KEY` to talk to the Zerops API. +- Your local app and shell use `zcli vpn up` to reach private service hostnames such as `db` or `cache`. -Full credential model: [Tokens and credentials](/zcp/security/tokens-and-project-access). +That means MCP can work while the app cannot reach the database. -## Local setup symptoms +| Symptom | Check | +| ------- | ----- | +| Agent does not list the `zerops` MCP server. | Relaunch the agent from the directory containing `.mcp.json`. | +| MCP startup says the token reaches multiple projects. | Replace `ZCP_API_KEY` with a single-project token. | +| Re-running `zcp init` made MCP disappear. | Re-add the `ZCP_API_KEY` env block to `.mcp.json` and restart the agent. | +| Local app cannot reach `db`, `cache`, or storage hostnames. | Run `zcli vpn up ` again. | +| Local app reads stale credentials. | Regenerate `.env`; it is a snapshot, not a live sync. | +| `zcp` is not found after install. | Add `~/.local/bin` or the install target to `PATH`, then restart the shell/agent. | -These apply to local setup. Remote setup is already on the private network and does not need VPN from your laptop. +For local setup details, use [Run locally](/zcp/setup/local-agent-bridge). -| Symptom | Likely cause | Next move | -| --------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -| Local app cannot reach `db`, `redis`, `cache`, or storage hostname | VPN is down or dropped after sleep/network change. | Run `zcli vpn up ` again. | -| `no route to host` or connection refused for managed-service hostname | Same network access issue. | Bring VPN up, then retry. If network works, regenerate `.env`. | -| Local app reads stale credentials | `.env` is a snapshot. | Ask ZCP to regenerate the `.env` after env changes. | -| Re-running `zcp init` made ZCP disappear from the agent | `.mcp.json` was rewritten without `ZCP_API_KEY`. | Re-add the `env` block and restart the agent from the app directory. | -| Agent does not list the `zerops` MCP server | Agent launched from the wrong directory or config not loaded. | Quit and relaunch from the directory containing `.mcp.json`. | -| `zcp` is not found after install | Install path is not on `PATH`. | Add `~/.local/bin` or the install target to your shell `PATH`, then restart the shell/agent. | +## Manual takeover -## Session drift and manual takeover +If you take over from the agent, read evidence in this order: -Use status when the chat-side picture drifts from reality: +1. Service list and runtime target. +2. Service-scoped events for the runtime in question. +3. Build logs for build failures, runtime logs for start or request failures. +4. Verify output for reachability and requested behavior. +5. Git history only when delivery uses git-push or CI. -```text -Read current project status, list the services in scope, and summarize the last deploy and verify result before changing anything. -``` - -That is the right move when the agent says it has no context, a browser workspace was reopened, a CLI session restarted, or you are taking over from a previous run. - -For manual takeover, read evidence in this order: - -| Evidence | Why it matters | -| --------------------------- | ------------------------------------------------------------------------------------------------- | -| Service-scoped events | Shows deploys, build/start transitions, failed process reasons, scaling, lifecycle actions. | -| Build logs and runtime logs | Separates build failures from runtime crashes and request-time app errors. | -| Verify output | Shows whether reachability and requested behavior actually passed. | -| Git history | Matters when delivery uses git-push; shows what source change corresponds to the deployed result. | +Do not inspect every service first. Start with the runtime in scope and expand only when the evidence points to a dependency. ## When to stop -Stop the loop when the agent cannot make progress with new evidence, needs a missing credential, is about to delete or replace a service, or the target runtime/stage is ambiguous. +Stop the loop when: + +- the same failure repeats without new evidence, +- the agent needs an external credential, +- the target runtime or stage choice is ambiguous, +- a destructive action would delete or replace a service, +- production release authority is needed, +- the request no longer fits the current project layout. -A repeated retry with the same evidence is not recovery. Ask the agent for the category, evidence read, fixes attempted, and next human decision. +A blocker is acceptable only when it names the runtime in scope, failure category, evidence read, fixes attempted, and the human decision or credential still needed. Before destructive recovery, read service-scoped events, logs, deploy/verify result, and git history when delivery uses git-push. ## Related -- [Build and ship](/zcp/workflows/build-with-zcp) -- [Workflow terms](/zcp/reference/agent-workflow) +- [Workflows in depth](/zcp/reference/agent-workflow) +- [ZCP MCP tools](/zcp/reference/mcp-operations) - [Tokens and credentials](/zcp/security/tokens-and-project-access) - [Run locally](/zcp/setup/local-agent-bridge) diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index a8adffbd4..78f6c5851 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -3,67 +3,31 @@ title: 'Remote or local setup' description: 'Where the zcp binary and agent workspace run: inside Zerops or on your machine.' --- -Remote setup is the default for most agent work with Zerops. Use local setup when the agent needs your local files, data, tools, editor, or a local-only agent client. - -Both setups use the same `zcp` binary and the same model: live state, bounded operations, and a finish line of proof or a blocker. The choice is where the agent workspace lives. - -That means Browser VS Code, a desktop editor over SSH, a local CLI agent, or another MCP-capable client are workspace choices. Once connected to the project, the agent works from the same Zerops state and evidence. - -:::tip Recommended default -**Remote setup** gives you an agent workspace inside Zerops, no local install, private networking, and preconfigured Claude Code + Browser VS Code when enabled. -::: - -## The short version - -
-
-

Remote setup

-

- The agent runs inside a zcp@1 service in Zerops. Use it when - you want the workspace, private networking, and broad shell permissions - contained away from your laptop. -

-
    -
  • No local install.
  • -
  • Claude Code and browser VS Code can be bundled.
  • -
  • Private services are reachable from the workspace.
  • -
  • Desktop remote editors can connect when you prefer them.
  • -
- Remote workspace → -
-
-

Local setup

-

- The agent runs from your machine. Use it when files, data, desktop editor, - git credentials, or a specific local agent client should stay local. -

-
    -
  • Install zcp locally.
  • -
  • Run zcp init in the working directory.
  • -
  • Add a Zerops token for one project.
  • -
  • Use zcli vpn up for private service access.
  • -
- Run ZCP locally → -
-
- -Remote setup usually adds one small workspace service. Local setup avoids that service, but deploy targets and managed services still cost while running. For most teams, the real decision is where the agent should safely and ergonomically work. - -:::info Public preview -Remote setup and local setup are both public preview. Local setup has more moving parts and may change faster: binary install path, `.mcp.json`, and `zcp init` artifacts are still settling between releases. -::: - -## What changes between setups - -| | Remote setup | Local setup | -| ---------------------- | -------------------------------------------------------------- | -------------------------------------------- | -| `zcp` process | Runs in Zerops as a `zcp@1` service. | Runs on your machine. | -| Agent process | Runs in the remote workspace when bundled or installed there. | Runs in your local editor or CLI. | -| Files the agent edits | Workspace files and mounted runtime files. | Files on your machine. | -| Private service access | Private Zerops network from the workspace. | Zerops VPN from your machine. | -| Token storage | Platform-injected into the workspace service. | `.mcp.json` in the local app directory. | -| Git credentials | Configured inside the workspace service. | Your local git credentials. | -| Safety posture | Agent shell permissions are contained in the workspace service. | Agent shell permissions affect your machine. | +Use this page to decide where the agent workspace lives: inside Zerops, or on your machine beside the local app directory. + +Both setups connect the agent to one Zerops project and the same project-scoped operations. The tradeoff is where the agent works: filesystem ownership, private-service access, credential location, bundled tooling, and shell blast radius. The runtime layout is a separate app-work decision. + +## What you are choosing + +**Remote setup** always includes the `zcp@1` workspace service. If the chosen **runtime layout** has a development runtime, the agent works with that service too. + +**Local setup** does not need the workspace service or a separate development runtime. It runs beside your **local source tree** and deploys to the Zerops runtime you choose to link. + +Remote setup is the **safer default** for broader agent autonomy because the agent shell, private networking, and workspace files stay **inside Zerops** instead of on your machine. Use local setup when local files, data, desktop tools, git credentials, or a **local-only agent client** need to stay local. + +## Setup comparison + +| | Remote setup | Local setup | +| ----------------------- | ------------------------------------------------- | -------------------------------------------- | +| Workspace service | Always has `zcp@1`. | Not required. | +| Development runtime | Only if the runtime layout includes one. | Not required for local work. | +| `zcp` process | Runs in Zerops. | Runs on your machine. | +| Agent process | Runs in the remote workspace. | Runs in your local editor or CLI. | +| Files the agent edits | Workspace files and mounted runtime files. | Files on your machine. | +| Private service access | Private Zerops network from the workspace. | Zerops VPN from your machine. | +| Token storage | Injected into the workspace service. | `.mcp.json` in the local app directory. | +| Git credentials | Configured inside the workspace service. | Your local git credentials. | +| Safety posture | Broad agent shell permissions stay in Zerops. | Agent shell permissions affect your machine. | ## Starting points @@ -81,9 +45,7 @@ Local setup can start from: ## Runtime layout is separate -Remote or local only answers where the agent and `zcp` process run. It does not decide the app runtime layout. - -The project can still use: +Remote or local only answers where the agent and `zcp` process run. The project can still use: - one mutable dev runtime, - a dev + stage pair, @@ -96,19 +58,13 @@ Stage is not production. Keep production in a separate Zerops project and promot ## Switching later -You can change setup later. - -- Start remote for evaluation, then move local when files or tools on your machine should own the workflow. -- Start local for a repo-first app, then add remote setup when you want an agent workspace inside Zerops. -- Keep both setups only when your team understands which source tree is authoritative for deploys. - -Before switching, make the handoff explicit: +Remote and local setup are not permanent, but switching is a handoff between workspaces, not a sync operation. Before switching, make the handoff explicit: -- Commit, push, or otherwise preserve the source tree that currently owns deploys. +- Preserve the current source tree through git or another explicit copy path. - Choose one deploy source for the next task: the remote workspace, mounted runtime files, or the local app directory. - Do not edit mounted runtime files and a local repo in parallel unless you have a merge plan. -- Regenerate local `.env` snapshots after project env changes. -- Relink the local deploy target if the runtime hostname changed. +- Regenerate local `.env` snapshots after Zerops env changes. +- If the local deploy target changed, have the agent inspect the current local link before deploying. ## Next steps diff --git a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx index ca3774724..f980b0738 100644 --- a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx +++ b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx @@ -1,6 +1,6 @@ --- title: 'Moved: Build and ship' -description: 'Infrastructure setup is now covered in Build and ship.' +description: 'Service setup is now covered in Build and ship.' unlisted: true --- @@ -8,4 +8,4 @@ This content now lives in [Build and ship](/zcp/workflows/build-with-zcp). Use that guide for runtime layout, existing services, missing services, and the handoff from setup into app work. -For exact route names and workflow vocabulary, use [Workflow terms](/zcp/reference/agent-workflow). +For workflow process details and exact labels, use [Workflows in depth](/zcp/reference/agent-workflow). diff --git a/apps/docs/content/zcp/workflows/delivery-handoff.mdx b/apps/docs/content/zcp/workflows/delivery-handoff.mdx index 37affb85b..bec61b1cb 100644 --- a/apps/docs/content/zcp/workflows/delivery-handoff.mdx +++ b/apps/docs/content/zcp/workflows/delivery-handoff.mdx @@ -8,4 +8,4 @@ This content now lives in [Build and ship](/zcp/workflows/build-with-zcp). Use that guide for the three user-facing delivery choices: keep direct deploy, push verified work to git, or hand off to CI/release tooling. -For exact internal labels, use [Workflow terms](/zcp/reference/agent-workflow#delivery-modes). +For delivery labels and workflow context, use [Workflows in depth](/zcp/reference/agent-workflow#delivery-after-proof). diff --git a/apps/docs/content/zcp/workflows/package-running-service.mdx b/apps/docs/content/zcp/workflows/package-running-service.mdx index 9d2df5017..a6dc779a5 100644 --- a/apps/docs/content/zcp/workflows/package-running-service.mdx +++ b/apps/docs/content/zcp/workflows/package-running-service.mdx @@ -3,150 +3,85 @@ title: 'Package a running service' description: 'Turn a running Zerops service into a re-importable bundle.' --- -Use packaging when a deployed runtime should become a reusable import bundle. This is a handoff or reuse task after a runtime already works, not the normal way to deploy the next app change. +Use packaging when a verified runtime should become a re-importable Zerops bundle. It is a handoff or reuse task after the app already works, not the normal way to deploy the next change. You can ask for it directly: ```text -Package appstage as a buildFromGit import bundle, commit it, and push it to git so I can import it into a fresh Zerops project. +Package appstage as a buildFromGit import bundle. +Commit it and push it to git. ``` -ZCP's export workflow takes a deployed runtime and prepares a single-repo bundle: source code, a project [import file](/references/import) named `zerops-project-import.yaml`, and a [`zerops.yaml`](/zerops-yaml/specification). The import file points back to the same source repo through `buildFromGit:`, so a fresh Zerops project can rebuild the app from git instead of carrying source code inside YAML. +The result is a single git repo that contains the app source, [`zerops.yaml`](/zerops-yaml/specification), and a project [import file](/references/import) named `zerops-project-import.yaml`. The import file uses `buildFromGit:` so a fresh Zerops project can rebuild the app from the repo instead of carrying source code inside YAML. -## 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 committed next to the app source. -- You want the new project to build from the same git repo through `buildFromGit:`, not from a copy of the code in YAML. -- You want to prepare a reusable starter, demo, handoff, or clean staging project. - -Packaging is **not** the right tool when: - -- You want the next app change deployed. For that, ask for the app result; direct deploy is the expected ZCP behavior behind the request. -- You want to create or update a production project. For production, export/import the project infrastructure and connect a production git or CI trigger; see [Promote to production](/zcp/workflows/promote-to-production). - -Packaging does not keep a long-running session. If interrupted, the agent reads live state and prepares the bundle again. You only need to decide when the runtime choice or env-var classification is ambiguous. - -## Pick the runtime to package +Packaging starts from a deployed Zerops runtime. If important source changes exist only on your laptop, deploy or push them first so the runtime and git repo match what you want to package. -Packaging captures **one** runtime. If your project has a dev runtime and a separate stage runtime, the agent asks which one to package. They may carry different env values, start commands, or `setup:` blocks. For single-runtime projects, the choice is implied. +## When to package -Managed services (`db`, `redis`, etc.) come along automatically as dependencies; you do not pick them one by one. +Package a runtime when you need: -A typical first prompt: +- a running runtime and its managed dependencies reproduced in another Zerops project, +- a reusable starter, demo, handoff, customer project, or clean staging baseline, +- a repo commit that carries the Zerops import shape next to the app source, +- the destination project to build from git. -```text -Package appstage as a buildFromGit import bundle, commit it, and push it so I can import it into a fresh Zerops project. -``` +Do not use packaging for: -The agent handles the packaging work; you only step in if it asks which runtime to package, which half of a dev+stage pair to use, how to classify a value, or how to configure git push. +- the next app deploy; use the normal build, deploy, and verify loop, +- production promotion; production needs its own infrastructure, credentials, domains, and release trigger, +- moving managed-service data; backups and restores are separate. -## Classify env vars +For production setup, follow [Promote to production](/zcp/workflows/promote-to-production). Packaging can help create a reusable app bundle, but it does not decide production infrastructure or release policy. -Once the runtime is chosen, ZCP prepares a candidate bundle and pauses on env vars. Each value needs a bucket so the generator knows what to do with it: +## What the agent prepares -| 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. | +The agent packages **one** runtime. If the project has dev and stage runtimes, it asks which one to use because they can have different env values, start commands, or `setup:` blocks. -ZCP does not guess these buckets from key names alone. The agent should classify them from source code: if the value is read by a Stripe SDK call it is `external-secret`; if it appears as `${db_*}` from a managed service it is `infrastructure`. The packaging response carries env _keys_ and redacts values; the agent fetches values only when it needs them. +Managed services come along as dependencies when the runtime's `zerops.yaml` needs their env references. You do not pick databases, caches, queues, or search services one by one. -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: +The workflow then prepares: -```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? -``` +- `zerops-project-import.yaml` - the project and service shape for the destination project, +- `zerops.yaml` - the runtime build and run configuration, +- a git commit and push when git delivery is configured or explicitly requested. -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**. The app source stays in git, and the export workflow adds or verifies two files at the repo root: - -```yaml -#zeropsPreprocessor=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 -``` +If the runtime has no usable `zerops.yaml` or git remote, the agent has to fix that first. A bundle is only useful when the destination project can pull source and build it from git. -```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} -``` +## Env vars need review -A few things worth noticing: +Project env vars are the main place where the agent may ask you to decide. Each value gets one bucket: -- The first line is the [Zerops preprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `<@...>` directives (here `<@generateRandomString(<32>)>` for `APP_KEY`), `#zeropsPreprocessor=on` must be line 1. Without it, the platform skips directive expansion at re-import and the literal string lands in the env var. The export workflow adds this 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). +| Bucket | Use for | Bundle result | +| --- | --- | --- | +| `infrastructure` | Values derived from managed services, such as database or cache references. | Omitted from project envs; the new managed service provides fresh values. | +| `auto-secret` | App-owned signing or encryption keys that can be regenerated. | Fresh generated secret on import. | +| `external-secret` | Third-party credentials such as Stripe, OpenAI, Mailgun, or GitHub. | `REPLACE_ME` placeholder. | +| `plain-config` | Literal non-secret config such as log level, feature flags, or public app settings. | Copied as-is. | -Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen delivery preference expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through normal [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). +Do not treat this as a key-name guessing game. The agent should inspect source code and ask when classification changes behavior. For example, regenerating a Laravel `APP_KEY`, Django `SECRET_KEY`, or session secret can break existing encrypted state. If state continuity matters, carry the existing value instead of generating a new one. -After the bundle is pushed, import `zerops-project-import.yaml` in the destination project through the dashboard or `zcli project project-import zerops-project-import.yaml`. The runtime entry in that import file uses `buildFromGit:` to clone and build the same repo. +## What is not included -For production setup, prefer the production promotion flow. Packaging creates a reusable app bundle; it does not decide production infrastructure, domains, secrets, release triggers, or scaling. See [Promote to production](/zcp/workflows/promote-to-production). +| Not included | What to do instead | +| --- | --- | +| Managed-service data | Restore from [Backup](/features/backup) or another migration path. | +| Real third-party secrets | Fill placeholders in the destination project before deploying. | +| Production domains, scaling, backups, and release triggers | Configure them in the destination project. | +| A repeatable production release process | Use your CI or release workflow; see [Promote to production](/zcp/workflows/promote-to-production). | -## What the bundle does not include +## Use the bundle -| 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 infrastructure, domains, and release triggers | Set up in the production Zerops project | +After the bundle is pushed, import `zerops-project-import.yaml` in the destination project through the dashboard or with `zcli project project-import zerops-project-import.yaml`. -## Packaging checks +Before opening the new project to users: -1. **Packaging is not delivery by itself.** It produces the bundle. Commit and push happen through your chosen [delivery preference](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) or the explicit packaging prompt. -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. +- fill `REPLACE_ME` values, +- restore or seed data when needed, +- review service scaling and public access, +- run a normal deploy and verification pass in the destination project. ## Next steps - [Build and ship](/zcp/workflows/build-with-zcp#package-a-verified-runtime) - where packaging fits into normal agent work. -- [How it works](/zcp/concept/how-it-works#verification-has-two-layers) - the verified runtime this packages. - [Promote to production](/zcp/workflows/promote-to-production) - create production infrastructure and release from git. +- [Import & Export YAML Configuration](/references/import) - Zerops import file reference. diff --git a/apps/docs/sidebars.js b/apps/docs/sidebars.js index d7485e949..e5bbcabab 100644 --- a/apps/docs/sidebars.js +++ b/apps/docs/sidebars.js @@ -698,7 +698,7 @@ module.exports = { { type: 'doc', id: 'zcp/reference/agent-workflow', - label: 'Workflow terms', + label: 'Workflows in depth', }, { type: 'doc', diff --git a/apps/docs/src/css/custom.css b/apps/docs/src/css/custom.css index 7e4c124a9..5c42fb2eb 100644 --- a/apps/docs/src/css/custom.css +++ b/apps/docs/src/css/custom.css @@ -536,49 +536,6 @@ html[data-theme='dark'] .docsearch-btn:hover { color: #5b45b0; } -.zcp-choice-card { - background: #ffffff; - border: 1px solid #d7dce5; - border-radius: 8px; - color: #172033; - display: flex; - flex-direction: column; - gap: 0.75rem; - padding: 1rem; -} - -.zcp-choice-card h3 { - font-size: 1rem; - line-height: 1.25; - margin: 0; -} - -.zcp-choice-card p { - color: #4b5563; - font-size: 0.88rem; - line-height: 1.5; - margin: 0; -} - -.zcp-choice-card ul { - display: grid; - gap: 0.42rem; - margin: 0; - padding-left: 1rem; -} - -.zcp-choice-card li { - color: #4b5563; - font-size: 0.84rem; - line-height: 1.4; -} - -.zcp-choice-card a { - align-self: flex-start; - font-size: 0.86rem; - font-weight: 650; -} - html[data-theme='dark'] .zcp-lifecycle-prompt { background: #132033; color: #f8fafc; @@ -638,16 +595,6 @@ html[data-theme='dark'] .zcp-lifecycle-stage--delivery { background: rgba(76, 29, 149, 0.22); } -html[data-theme='dark'] .zcp-choice-card { - background: rgba(24, 24, 27, 0.72); - color: #f8fafc; -} - -html[data-theme='dark'] .zcp-choice-card p, -html[data-theme='dark'] .zcp-choice-card li { - color: #cbd5e1; -} - @media (min-width: 768px) { .zcp-agent-flow { gap: 0.95rem; @@ -684,5 +631,4 @@ html[data-theme='dark'] .zcp-choice-card li { top: 50%; transform: translateY(-50%); } - } From 71853f6b32132d95e7461c141cd2d6815fc21349 Mon Sep 17 00:00:00 2001 From: krls2020 Date: Mon, 11 May 2026 17:01:24 +0200 Subject: [PATCH 17/24] Polish ZCP docs navigation structure --- .../features/local-remote-development.mdx | 14 ++++--- .../docs/content/zcp/concept/how-it-works.mdx | 2 +- apps/docs/content/zcp/quickstart.mdx | 33 +++++++++++++---- .../content/zcp/reference/agent-workflow.mdx | 6 ++- .../content/zcp/reference/mcp-operations.mdx | 6 +++ .../content/zcp/reference/troubleshooting.mdx | 10 ++--- .../security/tokens-and-project-access.mdx | 2 +- .../content/zcp/setup/choose-workspace.mdx | 6 +-- .../zcp/workflows/build-and-verify-app.mdx | 9 ++--- .../workflows/create-or-adopt-services.mdx | 8 ++-- .../zcp/workflows/delivery-handoff.mdx | 8 ++-- .../zcp/workflows/package-running-service.mdx | 23 ++++++++++-- .../zcp/workflows/promote-to-production.mdx | 37 ++++++++++++++++--- 13 files changed, 118 insertions(+), 46 deletions(-) diff --git a/apps/docs/content/features/local-remote-development.mdx b/apps/docs/content/features/local-remote-development.mdx index 56f2e6ce7..52e059132 100644 --- a/apps/docs/content/features/local-remote-development.mdx +++ b/apps/docs/content/features/local-remote-development.mdx @@ -3,6 +3,8 @@ title: Local & Remote Development description: Three ways to develop on Zerops — local with VPN, Cloud IDE, or your native IDE over SSH. Same network, same managed services, same pipeline as production. --- +import Icons from '@theme/Icon'; + Zerops development is network-first. Keep your editor on your laptop, work fully inside Zerops, or use a native editor over SSH; in each case you join the same project-private network and use the same hostnames, credentials, managed services, and deploy pipeline. Development, staging, and production can use the same Zerops-managed service types, networking model, and `zerops.yaml` patterns. What changes is environment policy: credentials, resource allocation, access rules, data, and release authority. @@ -124,9 +126,11 @@ If you've used GitHub Codespaces or Gitpod, Cloud IDE looks similar: a Linux con A hosted workspace is just a Zerops service. 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 +## Where to start -- Connect your laptop to a Zerops project → [VPN reference](/references/networking/vpn) -- Use a native editor over SSH → [SSH reference](/references/networking/ssh) -- Build and deploy from the same project model → [Build & deploy pipeline](/features/pipeline) -- Put a coding agent on top of this infrastructure → [Infrastructure for Coding Agents](/features/coding-agents) + diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx index d8486b7c1..d443e3ab6 100644 --- a/apps/docs/content/zcp/concept/how-it-works.mdx +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -184,7 +184,7 @@ A well-shaped run should: That is the practical difference between "the agent wrote code" and "the app task is done". -## Where to go deeper +## Next steps - [Build and ship](/zcp/workflows/build-with-zcp) - normal app work after setup. - [Workflows in depth](/zcp/reference/agent-workflow) - process gates, generated files, runtime layouts, delivery terms, and completion evidence. diff --git a/apps/docs/content/zcp/quickstart.mdx b/apps/docs/content/zcp/quickstart.mdx index fe064564a..54d84a3b6 100644 --- a/apps/docs/content/zcp/quickstart.mdx +++ b/apps/docs/content/zcp/quickstart.mdx @@ -103,10 +103,29 @@ If the agent cannot finish, useful output names the blocker: missing credential, ## Next steps -Read [How it works](/zcp/concept/how-it-works) for the model behind what just happened: live state, runtime fit, app wiring, deploy evidence, behavior proof, and blockers. - -Read [Remote or local setup](/zcp/setup/choose-workspace) when you are deciding where the agent workspace should live. Remote setup keeps the agent inside Zerops; local setup keeps it next to your files, editor, data, and tools. - -Read [Build and ship](/zcp/workflows/build-with-zcp) before giving the agent larger tasks. It explains the practical choices you control during normal work: runtime layout, app request, delivery, packaging, and production promotion. - -Before promoting anything to production, read [Promote to production](/zcp/workflows/promote-to-production). ZCP belongs in development or staging; production should receive verified work through your release process. + diff --git a/apps/docs/content/zcp/reference/agent-workflow.mdx b/apps/docs/content/zcp/reference/agent-workflow.mdx index 2f3887262..e51ae3543 100644 --- a/apps/docs/content/zcp/reference/agent-workflow.mdx +++ b/apps/docs/content/zcp/reference/agent-workflow.mdx @@ -210,4 +210,8 @@ A workflow-guided run is well-shaped if the evidence answers: | What proved behavior? | Endpoint, UI flow, job result, database/object state, or other requested proof. | | What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production handoff note. | -For operation names and direct tool calls, use [ZCP MCP tools](/zcp/reference/mcp-operations). For exact terms, use the [Glossary](/zcp/glossary). +## Next steps + +- [ZCP MCP tools](/zcp/reference/mcp-operations) - operation names and direct tool calls. +- [Troubleshooting](/zcp/reference/troubleshooting) - recovery order when a run gets stuck. +- [Glossary](/zcp/glossary) - exact terms used across the ZCP MCP reference. diff --git a/apps/docs/content/zcp/reference/mcp-operations.mdx b/apps/docs/content/zcp/reference/mcp-operations.mdx index a0977c879..28a264c52 100644 --- a/apps/docs/content/zcp/reference/mcp-operations.mdx +++ b/apps/docs/content/zcp/reference/mcp-operations.mdx @@ -95,3 +95,9 @@ Two operations require explicit care: - **Destructive import override** first refuses and names what would be replaced; a second call must acknowledge the same targets. See [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) for the user-facing confirmation flow. + +## Next steps + +- [Workflows in depth](/zcp/reference/agent-workflow) - how workflow-guided runs use these tools. +- [Troubleshooting](/zcp/reference/troubleshooting) - evidence order when a run gets stuck. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, and confirmation gates. diff --git a/apps/docs/content/zcp/reference/troubleshooting.mdx b/apps/docs/content/zcp/reference/troubleshooting.mdx index dcbf3faa0..fd2d0fbf4 100644 --- a/apps/docs/content/zcp/reference/troubleshooting.mdx +++ b/apps/docs/content/zcp/reference/troubleshooting.mdx @@ -93,9 +93,9 @@ A blocker is acceptable only when it names the runtime in scope, failure categor Before destructive recovery, read service-scoped events, logs, deploy/verify result, and git history when delivery uses git-push. -## Related +## Next steps -- [Workflows in depth](/zcp/reference/agent-workflow) -- [ZCP MCP tools](/zcp/reference/mcp-operations) -- [Tokens and credentials](/zcp/security/tokens-and-project-access) -- [Run locally](/zcp/setup/local-agent-bridge) +- [Workflows in depth](/zcp/reference/agent-workflow) - process gates, failure categories, and completion evidence. +- [ZCP MCP tools](/zcp/reference/mcp-operations) - operation names and what each tool can do. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, rotation, and destructive confirmations. +- [Run locally](/zcp/setup/local-agent-bridge) - local setup checks for MCP, VPN, and `.env` snapshots. 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 b69fac6c1..8f0bcf6f8 100644 --- a/apps/docs/content/zcp/security/tokens-and-project-access.mdx +++ b/apps/docs/content/zcp/security/tokens-and-project-access.mdx @@ -153,7 +153,7 @@ Threat model and boundaries: [Trust model](/zcp/security/trust-model). - **A rotated token needs a restart.** The live ZCP process keeps the old environment value until it starts again. - **A successful confirmation is still destructive.** Backups, git history, and service events are your recovery evidence; ZCP does not auto-rollback a confirmed deletion or replacement. -## Related +## Next steps - [Trust model](/zcp/security/trust-model) - the access boundary this page enforces. - [What remote workspace gives you](/zcp/setup/hosted-workspace) - automatic token injection. diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index 78f6c5851..143bb9ccf 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -68,6 +68,6 @@ Remote and local setup are not permanent, but switching is a handoff between wor ## Next steps -- [What remote workspace gives you](/zcp/setup/hosted-workspace) -- [Run locally](/zcp/setup/local-agent-bridge) -- [Trust model](/zcp/security/trust-model) +- [What remote workspace gives you](/zcp/setup/hosted-workspace) - remote setup, Browser VS Code, bundled agent CLI, and workspace persistence. +- [Run locally](/zcp/setup/local-agent-bridge) - local install, `zcp init`, `.mcp.json`, VPN, env snapshots, and deploy target linking. +- [Trust model](/zcp/security/trust-model) - how the safety boundary changes between remote setup and local setup. 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 1732eac91..2508710e9 100644 --- a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx +++ b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx @@ -1,11 +1,10 @@ --- title: 'Moved: Build and ship' description: 'Development and verification are now covered in Build and ship.' -unlisted: true --- -This content now lives in [Build and ship](/zcp/workflows/build-with-zcp). +This page moved to [Build and ship](/zcp/workflows/build-with-zcp). -Use that guide for the user-facing development controls. Use [How it works -> Verification has two layers](/zcp/concept/how-it-works#verification-has-two-layers) for the deeper verification model. - -For symptoms and recovery moves, use [Troubleshooting](/zcp/reference/troubleshooting). +- Use [Development](/zcp/workflows/build-with-zcp#develop-with-live-project-context) for app work, deploy, and verification. +- Use [How it works -> Verification has two layers](/zcp/concept/how-it-works#verification-has-two-layers) for the deeper verification model. +- Use [Troubleshooting](/zcp/reference/troubleshooting) for symptoms and recovery moves. diff --git a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx index f980b0738..72297b9c7 100644 --- a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx +++ b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx @@ -1,11 +1,9 @@ --- title: 'Moved: Build and ship' description: 'Service setup is now covered in Build and ship.' -unlisted: true --- -This content now lives in [Build and ship](/zcp/workflows/build-with-zcp). +This page moved to [Build and ship](/zcp/workflows/build-with-zcp). -Use that guide for runtime layout, existing services, missing services, and the handoff from setup into app work. - -For workflow process details and exact labels, use [Workflows in depth](/zcp/reference/agent-workflow). +- Use [Runtime layout](/zcp/workflows/build-with-zcp#choose-the-runtime-layout) for existing services, missing services, and the handoff from setup into app work. +- Use [Workflows in depth](/zcp/reference/agent-workflow) for workflow process details and exact labels. diff --git a/apps/docs/content/zcp/workflows/delivery-handoff.mdx b/apps/docs/content/zcp/workflows/delivery-handoff.mdx index bec61b1cb..ee47ccc3f 100644 --- a/apps/docs/content/zcp/workflows/delivery-handoff.mdx +++ b/apps/docs/content/zcp/workflows/delivery-handoff.mdx @@ -1,11 +1,9 @@ --- title: 'Moved: Build and ship' description: 'Delivery preference is now covered in Build and ship.' -unlisted: true --- -This content now lives in [Build and ship](/zcp/workflows/build-with-zcp). +This page moved to [Build and ship](/zcp/workflows/build-with-zcp). -Use that guide for the three user-facing delivery choices: keep direct deploy, push verified work to git, or hand off to CI/release tooling. - -For delivery labels and workflow context, use [Workflows in depth](/zcp/reference/agent-workflow#delivery-after-proof). +- Use [Delivery after proof](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) for the user-facing choices: keep direct deploy, push verified work to git, or hand off to CI/release tooling. +- Use [Workflows in depth](/zcp/reference/agent-workflow#delivery-after-proof) for delivery labels and workflow context. diff --git a/apps/docs/content/zcp/workflows/package-running-service.mdx b/apps/docs/content/zcp/workflows/package-running-service.mdx index a6dc779a5..233f56dee 100644 --- a/apps/docs/content/zcp/workflows/package-running-service.mdx +++ b/apps/docs/content/zcp/workflows/package-running-service.mdx @@ -82,6 +82,23 @@ Before opening the new project to users: ## Next steps -- [Build and ship](/zcp/workflows/build-with-zcp#package-a-verified-runtime) - where packaging fits into normal agent work. -- [Promote to production](/zcp/workflows/promote-to-production) - create production infrastructure and release from git. -- [Import & Export YAML Configuration](/references/import) - Zerops import file reference. + diff --git a/apps/docs/content/zcp/workflows/promote-to-production.mdx b/apps/docs/content/zcp/workflows/promote-to-production.mdx index 74acbd9a0..470ed78c4 100644 --- a/apps/docs/content/zcp/workflows/promote-to-production.mdx +++ b/apps/docs/content/zcp/workflows/promote-to-production.mdx @@ -107,8 +107,35 @@ Production hardening details live in the [Production Checklist for Zerops](/guid ## Next steps -- [Build and ship](/zcp/workflows/build-with-zcp#prepare-production-promotion) - where production promotion fits into normal agent work. -- [Production boundary](/zcp/security/production-policy) - why production stays outside the agent loop. -- [CI/CD with Zerops](/guides/ci-cd) - delivery options for production release paths. -- [Import & Export YAML Configuration](/references/import) - project export and import reference. -- [Public Access Configuration](/references/networking/public-access) - custom domains, DNS, SSL, and production public access. + From 6016f63a8d3c3f9cf5c0f9176b86a9a5819c3e2c Mon Sep 17 00:00:00 2001 From: krls2020 Date: Mon, 11 May 2026 17:36:47 +0200 Subject: [PATCH 18/24] Polish ZCP docs navigation --- .../features/local-remote-development.mdx | 1 - .../docs/content/zcp/concept/how-it-works.mdx | 1 - apps/docs/content/zcp/overview.mdx | 2 +- apps/docs/content/zcp/quickstart.mdx | 13 +++------- .../content/zcp/reference/agent-workflow.mdx | 2 +- .../content/zcp/reference/mcp-operations.mdx | 2 +- .../content/zcp/reference/troubleshooting.mdx | 9 +------ .../zcp/security/production-policy.mdx | 11 +++----- .../security/tokens-and-project-access.mdx | 2 +- .../docs/content/zcp/security/trust-model.mdx | 2 +- .../content/zcp/setup/hosted-workspace.mdx | 1 - .../content/zcp/setup/local-agent-bridge.mdx | 1 - .../content/zcp/workflows/build-with-zcp.mdx | 9 ------- .../zcp/workflows/package-running-service.mdx | 23 ---------------- .../zcp/workflows/promote-to-production.mdx | 26 ++++++------------- 15 files changed, 21 insertions(+), 84 deletions(-) diff --git a/apps/docs/content/features/local-remote-development.mdx b/apps/docs/content/features/local-remote-development.mdx index 52e059132..0f24185a4 100644 --- a/apps/docs/content/features/local-remote-development.mdx +++ b/apps/docs/content/features/local-remote-development.mdx @@ -132,5 +132,4 @@ A hosted workspace is just a Zerops service. You can SSH into it, install your o { type: 'link', href: '/references/networking/vpn', label: 'VPN reference', customProps: { icon: Icons['link'], description: 'Connect your laptop to a Zerops project-private network.' } }, { type: 'link', href: '/references/networking/ssh', label: 'SSH reference', customProps: { icon: Icons['command-line'], description: 'Use a native editor or shell over SSH.' } }, { type: 'link', href: '/features/pipeline', label: 'Build & deploy pipeline', customProps: { icon: Icons['circle-stack'], description: 'Build and deploy from the same project model.' } }, - { type: 'link', href: '/features/coding-agents', label: 'Infrastructure for Coding Agents', customProps: { icon: Icons['sparkles'], description: 'Put a coding agent on top of the same infrastructure.' } }, ]} /> diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx index d443e3ab6..379887f97 100644 --- a/apps/docs/content/zcp/concept/how-it-works.mdx +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -188,4 +188,3 @@ That is the practical difference between "the agent wrote code" and "the app tas - [Build and ship](/zcp/workflows/build-with-zcp) - normal app work after setup. - [Workflows in depth](/zcp/reference/agent-workflow) - process gates, generated files, runtime layouts, delivery terms, and completion evidence. -- [Troubleshooting](/zcp/reference/troubleshooting) - practical recovery when a run gets stuck. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index 1d085b382..1d2893276 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -92,4 +92,4 @@ Specific tasks and reference: ## What stays outside -This section is not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), [deployment lifecycle](/guides/deployment-lifecycle), networking, scaling, and service references remain canonical for platform behavior. +This section is not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), networking, scaling, and service references remain canonical for platform behavior. diff --git a/apps/docs/content/zcp/quickstart.mdx b/apps/docs/content/zcp/quickstart.mdx index 54d84a3b6..76b50422d 100644 --- a/apps/docs/content/zcp/quickstart.mdx +++ b/apps/docs/content/zcp/quickstart.mdx @@ -4,6 +4,7 @@ description: 'Try ZCP with a ready-made AI Agent recipe, Browser VS Code, Claude --- import Image from '/src/components/Image'; +import Icons from '@theme/Icon'; Use this when you want the fastest hands-on ZCP loop: deploy an **AI Agent** recipe, authorize Claude Code, open Browser VS Code, and ask for one product change. @@ -108,24 +109,18 @@ If the agent cannot finish, useful output names the blocker: missing credential, type: 'link', href: '/zcp/concept/how-it-works', label: 'How it works', - description: 'Live state, runtime fit, app wiring, deploy evidence, behavior proof, and blockers.', + customProps: { icon: Icons['cog-six-tooth'], description: 'Live state, runtime fit, app wiring, deploy evidence, behavior proof, and blockers.' }, }, { type: 'link', href: '/zcp/setup/choose-workspace', label: 'Remote or local setup', - description: 'Choose whether the agent workspace runs inside Zerops or beside your local files and tools.', + customProps: { icon: Icons['computer-desktop'], description: 'Choose whether the agent workspace runs inside Zerops or beside your local files and tools.' }, }, { type: 'link', href: '/zcp/workflows/build-with-zcp', label: 'Build and ship', - description: 'Runtime layout, development work, delivery, packaging, and production promotion.', - }, - { - type: 'link', - href: '/zcp/workflows/promote-to-production', - label: 'Promote to production', - description: 'Move verified dev or stage work into production without putting the zcp service there.', + customProps: { icon: Icons['circle-stack'], description: 'Runtime layout, development work, delivery, packaging, and production promotion.' }, }, ]} /> diff --git a/apps/docs/content/zcp/reference/agent-workflow.mdx b/apps/docs/content/zcp/reference/agent-workflow.mdx index e51ae3543..8517af9e8 100644 --- a/apps/docs/content/zcp/reference/agent-workflow.mdx +++ b/apps/docs/content/zcp/reference/agent-workflow.mdx @@ -210,7 +210,7 @@ A workflow-guided run is well-shaped if the evidence answers: | What proved behavior? | Endpoint, UI flow, job result, database/object state, or other requested proof. | | What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production handoff note. | -## Next steps +## Related reference - [ZCP MCP tools](/zcp/reference/mcp-operations) - operation names and direct tool calls. - [Troubleshooting](/zcp/reference/troubleshooting) - recovery order when a run gets stuck. diff --git a/apps/docs/content/zcp/reference/mcp-operations.mdx b/apps/docs/content/zcp/reference/mcp-operations.mdx index 28a264c52..6ed2443a4 100644 --- a/apps/docs/content/zcp/reference/mcp-operations.mdx +++ b/apps/docs/content/zcp/reference/mcp-operations.mdx @@ -96,7 +96,7 @@ Two operations require explicit care: See [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) for the user-facing confirmation flow. -## Next steps +## Related reference - [Workflows in depth](/zcp/reference/agent-workflow) - how workflow-guided runs use these tools. - [Troubleshooting](/zcp/reference/troubleshooting) - evidence order when a run gets stuck. diff --git a/apps/docs/content/zcp/reference/troubleshooting.mdx b/apps/docs/content/zcp/reference/troubleshooting.mdx index fd2d0fbf4..a575472fd 100644 --- a/apps/docs/content/zcp/reference/troubleshooting.mdx +++ b/apps/docs/content/zcp/reference/troubleshooting.mdx @@ -91,11 +91,4 @@ Stop the loop when: A blocker is acceptable only when it names the runtime in scope, failure category, evidence read, fixes attempted, and the human decision or credential still needed. -Before destructive recovery, read service-scoped events, logs, deploy/verify result, and git history when delivery uses git-push. - -## Next steps - -- [Workflows in depth](/zcp/reference/agent-workflow) - process gates, failure categories, and completion evidence. -- [ZCP MCP tools](/zcp/reference/mcp-operations) - operation names and what each tool can do. -- [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, rotation, and destructive confirmations. -- [Run locally](/zcp/setup/local-agent-bridge) - local setup checks for MCP, VPN, and `.env` snapshots. +Before destructive recovery, read service-scoped events, logs, deploy/verify result, and git history when delivery uses git-push. Token scope and destructive confirmations are covered in [Tokens and credentials](/zcp/security/tokens-and-project-access). diff --git a/apps/docs/content/zcp/security/production-policy.mdx b/apps/docs/content/zcp/security/production-policy.mdx index 0ad097390..89fdf5fbb 100644 --- a/apps/docs/content/zcp/security/production-policy.mdx +++ b/apps/docs/content/zcp/security/production-policy.mdx @@ -51,8 +51,6 @@ After stage verifies, ZCP's job is done for that change. Promotion happens outsi The agent can prepare the handoff by pushing code, summarizing verification evidence, and naming the runtime and URL it verified. It should not bridge development to production. -Broader CI/CD reference: [CI/CD with Zerops](/guides/ci-cd). - For the practical workflow, see [Promote to production](/zcp/workflows/promote-to-production). ## Credential rules @@ -88,11 +86,8 @@ The agent can still help before production promotion: ZCP's involvement stops at the handoff. Production execution belongs to the release process. -## Next steps +## Related security -- [Trust model](/zcp/security/trust-model) - the boundary that makes this policy enforceable. -- [Tokens and credentials](/zcp/security/tokens-and-project-access) - token handling. -- [Remote or local setup](/zcp/setup/choose-workspace) - compare development setups. -- [Build and ship](/zcp/workflows/build-with-zcp) - the dev/stage loop this policy assumes. - [Promote to production](/zcp/workflows/promote-to-production) - practical production promotion paths after ZCP proof. -- [Backup](/features/backup) - production recovery outside ZCP. +- [Trust model](/zcp/security/trust-model) - the boundary that makes this policy enforceable. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - production and development credentials stay separate. 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 8f0bcf6f8..266dd630e 100644 --- a/apps/docs/content/zcp/security/tokens-and-project-access.mdx +++ b/apps/docs/content/zcp/security/tokens-and-project-access.mdx @@ -153,7 +153,7 @@ Threat model and boundaries: [Trust model](/zcp/security/trust-model). - **A rotated token needs a restart.** The live ZCP process keeps the old environment value until it starts again. - **A successful confirmation is still destructive.** Backups, git history, and service events are your recovery evidence; ZCP does not auto-rollback a confirmed deletion or replacement. -## Next steps +## Related pages - [Trust model](/zcp/security/trust-model) - the access boundary this page enforces. - [What remote workspace gives you](/zcp/setup/hosted-workspace) - automatic token injection. diff --git a/apps/docs/content/zcp/security/trust-model.mdx b/apps/docs/content/zcp/security/trust-model.mdx index fc2e64739..5327124da 100644 --- a/apps/docs/content/zcp/security/trust-model.mdx +++ b/apps/docs/content/zcp/security/trust-model.mdx @@ -112,7 +112,7 @@ When taking over from an agent, read evidence in this order: Filter by service hostname when possible. Project-level timelines can include unrelated services and older failures. -## Next steps +## Related security - [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and confirmation gates. - [Remote or local setup](/zcp/setup/choose-workspace) - compare blast radius before setup. diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index 86191c8ba..0f0b87af3 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -119,4 +119,3 @@ Keep app runtime build steps with the runtime services. The remote workspace car - [Build and ship](/zcp/workflows/build-with-zcp) - normal app work after setup. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - how `ZCP_API_KEY`, git credentials, and CI secrets differ. -- [Production boundary](/zcp/security/production-policy) - why production belongs in a separate project. diff --git a/apps/docs/content/zcp/setup/local-agent-bridge.mdx b/apps/docs/content/zcp/setup/local-agent-bridge.mdx index 39a645ad8..78a3a2e69 100644 --- a/apps/docs/content/zcp/setup/local-agent-bridge.mdx +++ b/apps/docs/content/zcp/setup/local-agent-bridge.mdx @@ -171,6 +171,5 @@ The MCP server does not own your git credentials. In local setup, your local git ## Next steps -- [Remote or local setup](/zcp/setup/choose-workspace) - compare setups. - [Build and ship](/zcp/workflows/build-with-zcp) - normal app work after setup. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and destructive confirmations. diff --git a/apps/docs/content/zcp/workflows/build-with-zcp.mdx b/apps/docs/content/zcp/workflows/build-with-zcp.mdx index 3a0f4fad5..1d8fa4270 100644 --- a/apps/docs/content/zcp/workflows/build-with-zcp.mdx +++ b/apps/docs/content/zcp/workflows/build-with-zcp.mdx @@ -136,12 +136,3 @@ For a completed app task, the agent should report: - the delivery preference, packaging output, or production promotion state that now applies. If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the decision or credential needed from you. - -## Next steps - - diff --git a/apps/docs/content/zcp/workflows/package-running-service.mdx b/apps/docs/content/zcp/workflows/package-running-service.mdx index 233f56dee..bfb238bf3 100644 --- a/apps/docs/content/zcp/workflows/package-running-service.mdx +++ b/apps/docs/content/zcp/workflows/package-running-service.mdx @@ -79,26 +79,3 @@ Before opening the new project to users: - restore or seed data when needed, - review service scaling and public access, - run a normal deploy and verification pass in the destination project. - -## Next steps - - diff --git a/apps/docs/content/zcp/workflows/promote-to-production.mdx b/apps/docs/content/zcp/workflows/promote-to-production.mdx index 470ed78c4..621dd6c69 100644 --- a/apps/docs/content/zcp/workflows/promote-to-production.mdx +++ b/apps/docs/content/zcp/workflows/promote-to-production.mdx @@ -3,6 +3,8 @@ title: 'Promote to production' description: 'Move verified dev or stage work into a production Zerops project without putting the zcp service in production.' --- +import Icons from '@theme/Icon'; + Use this after the agent has proved work in development or stage. Production promotion is the point where authority changes: the agent can prepare proof, source changes, and setup notes, but production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. Production should be a separate Zerops project without a `zcp` service. That keeps the development agent out of the production blast radius while still letting verified work reach production. @@ -39,7 +41,7 @@ Before import, review the YAML as production infrastructure, not as a blind copy - plan managed-service data restore or migrations, - configure production domains, DNS, public access, SMTP, object storage, backups, queues, search, and cache as needed. -Before opening production traffic, get familiar with the deeper Zerops production surface: service modes, scaling, deploy pipeline, health checks, backups, public access, and domains. Start with the [Production Checklist for Zerops](/guides/production-checklist). If the app should use a real domain, follow [Public Access Configuration](/references/networking/public-access). +Before opening production traffic, get familiar with the deeper Zerops production surface: service modes, scaling, deploy pipeline, health checks, backups, public access, and domains. If the app should use a real domain, follow [Public Access Configuration](/references/networking/public-access). Project export/import creates the production infrastructure. Do not rely on it as the code delivery mechanism. The app source should be in git, and the first production code deploy should come through the production deploy trigger. @@ -103,39 +105,27 @@ A useful release handoff from the agent contains: If the answer only says that files were changed, the release handoff is incomplete. Ask for the verification evidence and the exact release artifact. -Production hardening details live in the [Production Checklist for Zerops](/guides/production-checklist). This page only defines the handoff from verified dev or stage work into production authority. +Production hardening belongs in your team's production checklist. This page only defines the handoff from verified dev or stage work into production authority. -## Next steps +## Related production references From f87966b9765c113601dcb71ba67f58088cd71ef9 Mon Sep 17 00:00:00 2001 From: Ales Rechtorik Date: Wed, 13 May 2026 11:40:19 +0200 Subject: [PATCH 19/24] Build out coding-agents intro page with topology visuals and landscape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite /features/coding-agents.mdx as the canonical intro for Zerops's Infrastructure for Coding Agents: explicit feature claims, a Bootstrap → Develop → human-reviewed PR-to-prod story, and a landscape section positioning ZCP against PaaS/VPS MCPs, CDEs, bundled agent platforms, local agents, sandboxes, and SDKs. New components: - IntroAgentVisual — ASCII grid with overlaid agent + recipe logos - CodingAgentsTopology — remote/local topology toggle - AsciiGraph — shared ASCII renderer with hl/accent variants - Note "tip" variant Plumbing: - markdown-source plugin so the ASCII visuals also render in the .md output served to LLMs (grid.cjs shared between React and converter) - Recipe and landscape image assets Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/docs/content/features/coding-agents.mdx | 284 +- .../features/local-remote-development.mdx | 39 +- apps/docs/package.json | 3 +- apps/docs/src/components/AsciiGraph/index.tsx | 24 + .../components/CodingAgentsTopology/build.cjs | 168 + .../components/CodingAgentsTopology/grid.cjs | 149 + .../components/CodingAgentsTopology/grid.tsx | 83 + .../components/CodingAgentsTopology/index.tsx | 53 + .../src/components/IntroAgentVisual/build.cjs | 136 + .../src/components/IntroAgentVisual/index.tsx | 38 + apps/docs/src/css/custom.css | 762 + .../__tests__/converters.test.js | 513 + .../src/plugins/markdown-source/converters.js | 649 + .../docs/src/plugins/markdown-source/index.js | 106 +- apps/docs/src/theme/MDXComponents/index.tsx | 2 + apps/docs/static/img/landscape/aider.png | Bin 0 -> 1891 bytes apps/docs/static/img/landscape/anthropic.ico | Bin 0 -> 15086 bytes apps/docs/static/img/landscape/aws.png | Bin 0 -> 3944 bytes apps/docs/static/img/landscape/bolt.png | Bin 0 -> 1088 bytes apps/docs/static/img/landscape/claude.png | Bin 0 -> 62828 bytes apps/docs/static/img/landscape/cline.png | Bin 0 -> 1530 bytes apps/docs/static/img/landscape/cloudflare.png | Bin 0 -> 908 bytes apps/docs/static/img/landscape/coder.svg | 1 + .../docs/static/img/landscape/codesandbox.svg | 1 + apps/docs/static/img/landscape/cursor.png | Bin 0 -> 7047 bytes apps/docs/static/img/landscape/daytona.ico | Bin 0 -> 15086 bytes apps/docs/static/img/landscape/devin.ico | Bin 0 -> 15086 bytes .../static/img/landscape/digitalocean.png | Bin 0 -> 1093 bytes apps/docs/static/img/landscape/e2b.ico | Bin 0 -> 4286 bytes apps/docs/static/img/landscape/fly.png | Bin 0 -> 1797 bytes apps/docs/static/img/landscape/github.png | Bin 0 -> 10942 bytes apps/docs/static/img/landscape/gitpod.ico | Bin 0 -> 15086 bytes apps/docs/static/img/landscape/hetzner.ico | Bin 0 -> 18854 bytes apps/docs/static/img/landscape/linode.png | Bin 0 -> 1248 bytes apps/docs/static/img/landscape/lovable.png | Bin 0 -> 16832 bytes apps/docs/static/img/landscape/mastra.png | Bin 0 -> 1146 bytes apps/docs/static/img/landscape/modal.png | Bin 0 -> 2705 bytes apps/docs/static/img/landscape/openhands.png | Bin 0 -> 2258 bytes apps/docs/static/img/landscape/railway.png | Bin 0 -> 23791 bytes apps/docs/static/img/landscape/render.svg | 1 + apps/docs/static/img/landscape/replit.png | Bin 0 -> 612 bytes apps/docs/static/img/landscape/v0.png | Bin 0 -> 1215 bytes apps/docs/static/img/landscape/vercel.ico | Bin 0 -> 15086 bytes apps/docs/static/img/landscape/vultr.png | Bin 0 -> 2500 bytes apps/docs/static/img/landscape/windsurf.png | Bin 0 -> 956 bytes apps/docs/static/img/landscape/zed.png | Bin 0 -> 2563 bytes apps/docs/static/img/recipes/astro.svg | 1 + apps/docs/static/img/recipes/claude.png | Bin 0 -> 62828 bytes apps/docs/static/img/recipes/codex.svg | 1 + apps/docs/static/img/recipes/django.svg | 1 + apps/docs/static/img/recipes/dotnet.svg | 1 + apps/docs/static/img/recipes/express.svg | 1 + apps/docs/static/img/recipes/fastapi.svg | 1 + apps/docs/static/img/recipes/flask.svg | 1 + apps/docs/static/img/recipes/gemini.svg | 1 + apps/docs/static/img/recipes/go.svg | 1 + apps/docs/static/img/recipes/laravel.svg | 1 + apps/docs/static/img/recipes/nestjs.svg | 1 + apps/docs/static/img/recipes/nextjs.svg | 1 + apps/docs/static/img/recipes/nodejs.svg | 1 + apps/docs/static/img/recipes/nuxt.svg | 1 + apps/docs/static/img/recipes/opencode.png | Bin 0 -> 1541 bytes apps/docs/static/img/recipes/php.svg | 1 + apps/docs/static/img/recipes/python.svg | 1 + apps/docs/static/img/recipes/rails.svg | 1 + apps/docs/static/img/recipes/remix.svg | 1 + apps/docs/static/img/recipes/rust.svg | 1 + apps/docs/static/img/recipes/spring.svg | 1 + apps/docs/static/img/recipes/svelte.svg | 1 + apps/docs/static/img/recipes/symfony.svg | 1 + .../docs/static/img/recipes/zerops/analog.svg | 28 + .../static/img/recipes/zerops/angular.png | Bin 0 -> 6815 bytes apps/docs/static/img/recipes/zerops/astro.svg | 3 + apps/docs/static/img/recipes/zerops/bun.svg | 1 + apps/docs/static/img/recipes/zerops/deno.svg | 1 + .../docs/static/img/recipes/zerops/dotnet.svg | 1 + apps/docs/static/img/recipes/zerops/gleam.svg | 7 + .../docs/static/img/recipes/zerops/golang.svg | 60 + apps/docs/static/img/recipes/zerops/java.svg | 1 + .../static/img/recipes/zerops/laravel.svg | 1 + .../docs/static/img/recipes/zerops/nestjs.svg | 1 + .../docs/static/img/recipes/zerops/nextjs.svg | 8 + .../docs/static/img/recipes/zerops/nodejs.svg | 1 + apps/docs/static/img/recipes/zerops/nuxt.svg | 3 + apps/docs/static/img/recipes/zerops/php.svg | 7 + .../docs/static/img/recipes/zerops/python.svg | 1 + apps/docs/static/img/recipes/zerops/qwik.svg | 27 + apps/docs/static/img/recipes/zerops/react.svg | 9 + apps/docs/static/img/recipes/zerops/ruby.svg | 948 + apps/docs/static/img/recipes/zerops/rust.svg | 4 + apps/docs/static/img/recipes/zerops/solid.svg | 1 + .../docs/static/img/recipes/zerops/svelte.svg | 20 + apps/docs/static/img/recipes/zerops/vue.svg | 2 + apps/docs/static/llms-full.txt | 21492 +++++++++++++--- apps/docs/static/llms-small.txt | 17407 +++++++++++-- apps/docs/static/llms.txt | 4 + .../src/components/Note/Layout/index.tsx | 6 +- .../docs-ui/src/components/Note/Types/tip.tsx | 23 + .../docs-ui/src/components/Note/index.tsx | 5 +- zerops-llm-script.ts | 129 +- 100 files changed, 36525 insertions(+), 6710 deletions(-) create mode 100644 apps/docs/src/components/AsciiGraph/index.tsx create mode 100644 apps/docs/src/components/CodingAgentsTopology/build.cjs create mode 100644 apps/docs/src/components/CodingAgentsTopology/grid.cjs create mode 100644 apps/docs/src/components/CodingAgentsTopology/grid.tsx create mode 100644 apps/docs/src/components/CodingAgentsTopology/index.tsx create mode 100644 apps/docs/src/components/IntroAgentVisual/build.cjs create mode 100644 apps/docs/src/components/IntroAgentVisual/index.tsx create mode 100644 apps/docs/src/plugins/markdown-source/__tests__/converters.test.js create mode 100644 apps/docs/src/plugins/markdown-source/converters.js create mode 100644 apps/docs/static/img/landscape/aider.png create mode 100644 apps/docs/static/img/landscape/anthropic.ico create mode 100644 apps/docs/static/img/landscape/aws.png create mode 100644 apps/docs/static/img/landscape/bolt.png create mode 100644 apps/docs/static/img/landscape/claude.png create mode 100644 apps/docs/static/img/landscape/cline.png create mode 100644 apps/docs/static/img/landscape/cloudflare.png create mode 100644 apps/docs/static/img/landscape/coder.svg create mode 100644 apps/docs/static/img/landscape/codesandbox.svg create mode 100644 apps/docs/static/img/landscape/cursor.png create mode 100644 apps/docs/static/img/landscape/daytona.ico create mode 100644 apps/docs/static/img/landscape/devin.ico create mode 100644 apps/docs/static/img/landscape/digitalocean.png create mode 100644 apps/docs/static/img/landscape/e2b.ico create mode 100644 apps/docs/static/img/landscape/fly.png create mode 100644 apps/docs/static/img/landscape/github.png create mode 100644 apps/docs/static/img/landscape/gitpod.ico create mode 100644 apps/docs/static/img/landscape/hetzner.ico create mode 100644 apps/docs/static/img/landscape/linode.png create mode 100644 apps/docs/static/img/landscape/lovable.png create mode 100644 apps/docs/static/img/landscape/mastra.png create mode 100644 apps/docs/static/img/landscape/modal.png create mode 100644 apps/docs/static/img/landscape/openhands.png create mode 100644 apps/docs/static/img/landscape/railway.png create mode 100644 apps/docs/static/img/landscape/render.svg create mode 100644 apps/docs/static/img/landscape/replit.png create mode 100644 apps/docs/static/img/landscape/v0.png create mode 100644 apps/docs/static/img/landscape/vercel.ico create mode 100644 apps/docs/static/img/landscape/vultr.png create mode 100644 apps/docs/static/img/landscape/windsurf.png create mode 100644 apps/docs/static/img/landscape/zed.png create mode 100644 apps/docs/static/img/recipes/astro.svg create mode 100644 apps/docs/static/img/recipes/claude.png create mode 100644 apps/docs/static/img/recipes/codex.svg create mode 100644 apps/docs/static/img/recipes/django.svg create mode 100644 apps/docs/static/img/recipes/dotnet.svg create mode 100644 apps/docs/static/img/recipes/express.svg create mode 100644 apps/docs/static/img/recipes/fastapi.svg create mode 100644 apps/docs/static/img/recipes/flask.svg create mode 100644 apps/docs/static/img/recipes/gemini.svg create mode 100644 apps/docs/static/img/recipes/go.svg create mode 100644 apps/docs/static/img/recipes/laravel.svg create mode 100644 apps/docs/static/img/recipes/nestjs.svg create mode 100644 apps/docs/static/img/recipes/nextjs.svg create mode 100644 apps/docs/static/img/recipes/nodejs.svg create mode 100644 apps/docs/static/img/recipes/nuxt.svg create mode 100644 apps/docs/static/img/recipes/opencode.png create mode 100644 apps/docs/static/img/recipes/php.svg create mode 100644 apps/docs/static/img/recipes/python.svg create mode 100644 apps/docs/static/img/recipes/rails.svg create mode 100644 apps/docs/static/img/recipes/remix.svg create mode 100644 apps/docs/static/img/recipes/rust.svg create mode 100644 apps/docs/static/img/recipes/spring.svg create mode 100644 apps/docs/static/img/recipes/svelte.svg create mode 100644 apps/docs/static/img/recipes/symfony.svg create mode 100644 apps/docs/static/img/recipes/zerops/analog.svg create mode 100644 apps/docs/static/img/recipes/zerops/angular.png create mode 100644 apps/docs/static/img/recipes/zerops/astro.svg create mode 100644 apps/docs/static/img/recipes/zerops/bun.svg create mode 100644 apps/docs/static/img/recipes/zerops/deno.svg create mode 100644 apps/docs/static/img/recipes/zerops/dotnet.svg create mode 100644 apps/docs/static/img/recipes/zerops/gleam.svg create mode 100644 apps/docs/static/img/recipes/zerops/golang.svg create mode 100644 apps/docs/static/img/recipes/zerops/java.svg create mode 100644 apps/docs/static/img/recipes/zerops/laravel.svg create mode 100644 apps/docs/static/img/recipes/zerops/nestjs.svg create mode 100644 apps/docs/static/img/recipes/zerops/nextjs.svg create mode 100644 apps/docs/static/img/recipes/zerops/nodejs.svg create mode 100644 apps/docs/static/img/recipes/zerops/nuxt.svg create mode 100644 apps/docs/static/img/recipes/zerops/php.svg create mode 100644 apps/docs/static/img/recipes/zerops/python.svg create mode 100644 apps/docs/static/img/recipes/zerops/qwik.svg create mode 100644 apps/docs/static/img/recipes/zerops/react.svg create mode 100644 apps/docs/static/img/recipes/zerops/ruby.svg create mode 100644 apps/docs/static/img/recipes/zerops/rust.svg create mode 100644 apps/docs/static/img/recipes/zerops/solid.svg create mode 100644 apps/docs/static/img/recipes/zerops/svelte.svg create mode 100644 apps/docs/static/img/recipes/zerops/vue.svg create mode 100644 packages/docs-ui/src/components/Note/Types/tip.tsx diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx index 90eca8779..1c2414c73 100644 --- a/apps/docs/content/features/coding-agents.mdx +++ b/apps/docs/content/features/coding-agents.mdx @@ -1,143 +1,219 @@ --- title: 'Infrastructure for Coding Agents' -description: 'Zerops gives coding agents the project infrastructure real app work needs: runtimes, managed services, private networking, deploys, logs, verification, and handoff.' +description: 'Real project infrastructure for coding agents — runtimes, managed services, private networking, deploys, logs, and verification, exposed through MCP.' --- import Icons from '@theme/Icon'; +import CodingAgentsTopology from '@site/src/components/CodingAgentsTopology'; +import IntroAgentVisual from '@site/src/components/IntroAgentVisual'; -Coding agents can write code anywhere. Real application work needs more than an editor or a sandbox: the agent needs a project to operate, services to connect to, deploys to run, logs to inspect, and evidence that the requested behavior works. +Zerops was built on the idea of **environment parity** — giving developers the full development lifecycle, from remote development to highly available production, with the observability and developer tools for maximum flexibility, and sensible defaults so the configs stay reasonable. Turns out that's **exactly what coding agents need** to produce and iterate on production-ready applications. -Zerops gives the agent that infrastructure surface. A Zerops project contains the runtimes, managed services, private networking, env vars, deployment pipeline, logs, and public access that the app actually uses. The agent can work against the same kind of infrastructure a human developer or staging release would use, not a pasted description of it. +

-**ZCP MCP** is the MCP tool layer that lets an agent read and operate one Zerops project through bounded project-scoped tools. The useful split is simple: **you hold product intent and judgment, Zerops holds project reality, and the MCP layer lets the agent work with that reality.** +## What is it -
-
- You - Product intent -

Outcome, constraints, acceptance criteria, credentials, approvals.

-
-
- Coding agent - App work -

Code changes, config wiring, deploy decisions, visible reasoning.

-
-
- ZCP MCP - Project-scoped tools -

Live state, operations, evidence, optional workflow instructions.

-
-
- Zerops project - Project reality -

Runtimes, services, private network, env refs, deploys, logs, URLs.

-
-
- Result - Proof or blocker -

URL, endpoint result, UI state, worker result, stored data, evidence.

-
-
+An **[MCP server](/zcp/overview)** for your agent — running locally or in a remote container — that makes use of the flexibility and processes Zerops provides. There's no "Zerops system prompt" hogging your context window. The MCP server is a thin layer that makes **your agent a Zerops platform power user** — when needed — so it understands: -## For agent work that has to run +- how networking, scaling, debugging, build & deployment, environment variables, and service provisioning work on the platform +- the development loop: start a dev server → implement → deploy to stage → verify -Use this when a coding agent is expected to produce a running Zerops app, not just files, a patch, or a preview artifact. +The depth of the platform underneath is what gives the agent room to actually work. A single agent can scaffold and iterate on projects with multiple runtimes (`app`, `api`, `worker`) backed by multiple [managed services](/features/infrastructure) (object storage `storage`, Postgres `db`, Elasticsearch `search`, NATS `broker`, Valkey `cache`) — just as easily as it can work on a simple Next.js presentational website. -You still decide the product outcome, stack constraints, acceptance criteria, repository policy, external credentials, and risky approvals. The agent gets the Zerops state and operations needed to use or create services, wire the app to managed infrastructure, deploy, verify, recover from failures, and report proof or a blocker. +A **[recipe ecosystem](https://app.zerops.io/recipes)** covering the most popular frameworks — pre-prepared for the whole lifecycle — gives the agent a baseline to start from, or a guide for setting up any stack on Zerops. -The agent can run in a Zerops-hosted workspace or from your local editor or CLI. The workspace is not the point; real infrastructure is. + -## What infrastructure gives the agent + -Without project infrastructure, an app prompt turns into an operations runbook. With Zerops infrastructure and ZCP MCP, the prompt can stay closer to the product because the agent can read and operate the project directly. +Most coding-agent platforms went agent-first, then bolted on infrastructure — where it runs, how it reaches the database, how its output becomes production-ready, which third-party services to wire up. **Zerops went platform-first.** The complete development lifecycle existed before coding agents did; we taught them to operate it — **not just how to call APIs, but how to ship software the way a senior developer would.** -| Capability | What the agent gets | Why it matters | -| ---------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | -| **Live project state** | Runtime services, managed services, env-var keys, recent events, and deploy status. | The agent does not need a pasted service map or stale chat memory. | -| **Real dependencies** | Managed Postgres, Valkey, NATS, object storage, search, mail, and other services. | Product requirements can become real infrastructure, not local mocks. | -| **Private networking** | Services reachable by project hostnames and injected credentials. | App wiring follows the platform model the runtime actually uses. | -| **Deploy pipeline** | Builds and deploys through Zerops instead of a preview-only sandbox. | The first working result is already running on the platform. | -| **Logs and events** | Build logs, runtime logs, service status, and platform events. | Failure becomes an evidence surface instead of guesswork. | -| **Verification target**| Real URLs, endpoints, workers, databases, and stored objects. | Done means behavior proof, not just "the agent wrote code." | -| **Delivery path** | Direct deploy, git handoff, CI setup, packaging, or production promotion. | Verified work can move into the team's normal release process. | + -A healthy run ends in one of two states: proof or a blocker. Proof is an inspectable URL, endpoint response, UI state, worker result, or stored data that shows the requested behavior works. A blocker names the missing credential, decision, unsupported fit, or repeated failure with the relevant evidence. +

-## What the MCP layer adds +## Main features -Zerops is the infrastructure: runtimes, managed services, private networking, env vars, deploys, logs, and public access. ZCP MCP is the tool surface for one selected Zerops project. +### Your agent, your subscription -The tools give the agent operations, not judgment: discover services, manage configuration, deploy, inspect logs and events, recover from failures, and verify behavior against real services. Running `zcp` remotely or locally changes the workspace, filesystem, and network boundary, not the project model. +Bring whichever agent you already use. Claude Code today; Codex, Gemini CLI, opencode, and any MCP-capable client next. The container is a regular Ubuntu — install your own CLI tools, drop in your `.claude` / `.cursor` configs, attach your IDE over SSH. **Zerops doesn't resell tokens or proxy your model calls** — sign in with your own Anthropic / OpenAI / Google subscription and the agent talks to its provider directly. -Generated workflow instructions are a separate layer. They teach sequencing: inspect state before changing things, choose the runtime target, use existing services or create missing ones, wire app code and Zerops config together, deploy, read failure evidence, verify requested behavior, and stop with proof or a blocker. +### An ordinary Zerops project underneath -You can also use ZCP MCP without the workflow instructions for custom MCP clients, scripts, dashboards, policy-gated clients, or narrow operational tasks. Then your client owns sequencing, verification, and when to ask a human. +The agent operates inside a normal Zerops project — same shape as production. Managed databases (PostgreSQL, MariaDB, ClickHouse), key-value stores (KeyDB, Valkey), search (Elasticsearch, Meilisearch, Typesense), vector store (Qdrant), message queues (NATS, Kafka), object and shared storage, managed Nginx — all on a private network, addressable by hostname. The same pipeline that deploys here can deploy to a separate HA production project with no `zcp` service attached. Not a sandbox the work outgrows. -For setup and tool details: +### Human ↔ agent handover -- [MCP overview](/zcp/overview) -- [Remote or local setup](/zcp/setup/choose-workspace) -- [MCP tools](/zcp/reference/mcp-operations) +The agent and the human share the same workspace — same filesystem, same SSH key, same Cloud IDE in the browser, same remote-dev attach from your local IDE. Take over mid-session, set something up before the agent runs, debug in the same container the agent just left. Nothing reaches production until you merge the PR — the agent opens it, you gate it. -## Remote or local +### Recipes for any stack -Remote setup runs `zcp` inside a Zerops `zcp@1` service and can include the bundled coding agent and Browser VS Code. Local setup runs `zcp` on your machine so app files and git credentials stay local, with private service access through Zerops VPN. +Runtimes for Bun, Deno, Node.js, Go, Python, Rust, Java, .NET, PHP, Elixir, Gleam — with curated recipes for the framework on top: Next.js, Nuxt, Astro, Svelte, React, Vue, Angular, Solid, Qwik, Analog, NestJS, Laravel, and more. Pick a recipe, select the **AI Agent** environment, and the project comes up with a working dev runtime, a stage runtime, the right managed services wired in, and a coding agent briefed on the Zerops surface. -Both paths expose the same project-scoped Zerops operations. Choose by where the agent and source tree should live, not by which infrastructure the app gets. For the broader human development model, see [Local & Remote Development](/features/local-remote-development). +

-## Why Zerops is legible to agents +## How it works in practice -Agents reason over text and tool results. Zerops is useful for agents because the platform is explicit in the same places it is useful for humans: +The agent reaches your project's private network one of two ways: -- `zerops.yaml` is structured application and deploy configuration. -- Services have names, ports, env vars, and dependencies the agent can inspect. -- Managed-service references use platform-visible hostnames and credentials. -- Logs, deploy events, and service status are queryable. -- Runtime files can be reached through SSH or workspace mounts when that setup is used. -- Development, staging, and production can use the same Zerops-managed service types and platform model. +- **Remote** — a `zcp` service deployed into the project runs the agent. You attach to it with Claude Code's IDE extension, with any IDE that supports remote development (Cursor, Windsurf, VS Code Remote), or by running [`zcli vpn up`](/references/networking/vpn) and ssh-ing into `zcp` to drive the rest of the project from a shell. +- **Local** — install the `zcp` MCP on your machine and run `zcli vpn up` to join the network. From there your IDE, agent, and shell can ssh directly into any container. -The MCP layer packages those surfaces into agent-friendly operations, but the underlying value is the project infrastructure itself. +Either way, the agent reaches [managed services by hostname](/references/networking/internal-access) (`db:5432`, `cache:6379`), deploys through the [Zerops pipeline](/features/pipeline), and reads logs and events the same way you would. -## Boundaries and permissions + -The boundary is the project. The MCP server uses a project-scoped Zerops token, so the agent can operate only the project selected for that setup. Account-wide, organization-wide, and cross-project actions stay outside that token. +The agent runs through two workflows. **Bootstrap** settles which services and runtime targets the app needs. **Develop** is the short work loop — edit code on `appdev`, deploy to `appstage`, check it's reachable, verify behavior against `db:5432` and `cache:6379`, fix from evidence. A session ends with a URL you can open, or a specific blocker — failure category, attempts, what's still needed. Same hostnames, same managed services, same pipeline as production — just at smaller scale. See [Workflows in depth](/zcp/reference/agent-workflow) for both. -Mutating operations still need to fit your agent policy and team rules. Destructive actions have extra confirmation gates. Git providers, CI, payment services, and production release credentials remain separate. - -For production, use coding-agent access in development or staging projects. Production should receive promoted work through your release or CI process, not through an agent session with broad project operations. See [Production boundary](/zcp/security/production-policy). - -Security details live in reference: - -- [Trust model](/zcp/security/trust-model) -- [Tokens and credentials](/zcp/security/tokens-and-project-access) - -## What this is not - -Infrastructure for coding agents is not a prompt-to-prototype generator, an isolated code execution sandbox, or a cloud IDE with autocomplete. Those can be useful, but they do not by themselves give an agent the operational surface of a real app project. - -The claim is narrower: when the output must become a verified app on Zerops, the agent needs project infrastructure plus a safe way to read and operate it. - -## How it differs from adjacent tools - -Most adjacent products solve a real part of agent work. The gap is usually what happens after code exists: where the app runs, which services it uses, whether the environment matches the release target, and how failures are diagnosed. - -| | Persistent workspace | Real services | Deploy pipeline | Env parity | Standard tooling | -| --------------------- | :------------------: | :-----------: | :-------------: | :--------: | :--------------: | -| One-shot generators | - | - | - | - | - | -| Replit | ✓ | ~ | ~ | - | - | -| E2B | - | - | - | - | ✓ | -| Codespaces / Gitpod | ✓ | - | - | - | ✓ | -| Cloudflare-style | - | ~ | ~ | - | - | -| **ZCP** | **✓** | **✓** | **✓** | **✓** | **✓** | - -`~` means the category can cover part of the need, but not as one project model for agent-driven app work. Zerops does not replace the coding agent, and the MCP layer does not replace the developer. The difference is where the agent works: against the project infrastructure that will run the app. +{` `}{`zcp (local or remote)`}{` + │ + ▼ + edit code on `}{`appdev`}{` ◀───────────────────────┐ + └ dev server, hot reload │ + │ │ + ▼ │ + deploy to `}{`appstage`}{` │ + │ │ + ▼ │ + check reachability │ + │ │ + ▼ │ + verify behavior ─── fail ─────┐ │ + │ │ │ + │ pass ▼ │ + ▼ read logs / events │ + URL (proof) fix from evidence ─────┘`} + +Once the loop produces verified work, the agent doesn't push to production — it opens a PR from the dev project. **A human review gates the merge**, then a release tag or merge triggers automatic CI/CD into a [separate production project](/zcp/security/production-policy) that has no `zcp` service at all. + +{` Dev project GitHub Prod project + +┌─────────────────────┐ ┌──────────────┐ ┌─────────────────────┐ +│ appdev │ │ │ │ app (HA) │ +│ appstage │ │ Pull │ │ │ +│ │ push │ Request │ CI/CD │ db (HA) │ +│ db, cache │ ────▶ │ │ ────▶ │ │ +│ │ │ `}{`+ human`}{` │ │ cache (HA) │ +│ `}{`zcp service`}{` │ │ `}{`review`}{` │ │ │ +│ │ │ │ │ (no zcp service) │ +└─────────────────────┘ └──────────────┘ └─────────────────────┘`} + +

+ +## Where does this fit in the agent landscape + +With the model clear, here's where ZCP sits among adjacent tools. People say "agent infrastructure," "infra for Claude agents," "agent hosting," or "agent sandboxes" to mean different things — usually one of the categories below. + +
+
+

PaaS MCPs

+
+ AWS + Railway + Fly.io + Render +
+

AWS MCP servers · Railway MCP · Fly.io MCP · Render MCP

+
+

Most major PaaS providers have shipped an MCP — provision services, deploy code, list resources, read operational state. Great for "let my agent ship to my Railway account." These are *deploy and operate* surfaces, driven from outside the platform — and they reflect what's underneath: platforms optimized for day-one deploy that tuck the infrastructure behind a wall. Once an agent needs to actually *develop* against that infrastructure — debug a failed health check, follow real logs, hit Postgres from inside the network, inspect what a build is doing, run the dev loop before it ships anything — the wall stops it. That's by design on those platforms, not a missing feature.

+ +

ZCP exposes the full **Bootstrap and Develop** loop as MCP-addressable primitives because Zerops was built around the development lifecycle from the start — not a dev loop retrofitted onto a deploy-first platform.

+
+ +
+
+

Cloud VPS MCPs

+
+ Hetzner + DigitalOcean + Linode + Vultr +
+

Hetzner Cloud MCP · DigitalOcean MCP · Linode MCP · raw SSH/server MCPs

+
+

"Let your agent manage your Hetzner VPS." An MCP gives the agent access to a Linux box — provision it, SSH in, install Docker and Postgres and nginx, set up systemd units, point a domain at it. Maximum control, lowest cost, full Linux to play with. The same reason senior developers reach for managed platforms applies here: even if you *can* harden SSH, write nginx configs, tune `pg_hba.conf`, rotate TLS, set firewall rules, run fail2ban, and stay on top of CVEs across kernel, OpenSSL, Docker, Postgres, and nginx — you usually don't want to. Each layer is its own domain with its own gotchas, each is a security surface that drifts the moment you stop tending it, and every project becomes a snowflake. Hand that work to an agent and you also hand it the blast radius: misconfigured firewall, debug endpoint left open, port 5432 exposed to the internet, keys never rotated. ZCP is the inverse trade-off — the **guardrails** of a managed platform. Private networking, TLS, isolated services, safe defaults, managed backups, no exposed ports unless you opt in — all built in. The agent operates through [bounded MCP tools](/zcp/reference/mcp-operations) instead of `sudo` on a fresh Ubuntu box, so the mistakes that matter on a VPS aren't reachable from where it sits.

+
+ +
+
+

Cloud dev environments with AI

+
+ GitHub Codespaces + Gitpod + Coder +
+

GitHub Codespaces + Copilot Workspace · Gitpod · Coder

+
+

A managed Linux container in the cloud with your editor and an AI assistant attached. Solves "where the agent runs" so state survives between sessions and machine setup stops being a question. The `zcp` service is the closest thing in this category — managed Linux container, Cloud IDE, your agent of choice — except it lives *inside* a Zerops project, addressing the project's runtimes, database, and cache by hostname, with deploys and logs as primitives the agent can call.

+
+ +
+
+

Bundled agent platforms

+
+ Replit Agent + Lovable + Bolt.new + v0 + Devin + OpenHands +
+

Replit Agent · Lovable · Bolt.new · v0 · Devin · OpenHands

+
+

The agent, the workspace, the stack, and (often) the hosting target are fused into one closed product. Fast for prototypes. Decisions are made for you. Switching agents usually means switching platforms, and production-grade primitives — multi-environment promotion, private networking, real observability — tend to be limited or absent. **ZCP doesn't bundle**: the agent and model stay yours on your own subscription, and the platform underneath is normal Zerops with the full stack of managed services, networking, deploys, and observability.

+
+ +
+
+

Local coding agents

+
+ Cursor + Claude Code + Windsurf + Zed + Aider + Cline +
+

Cursor · Windsurf · Zed · Claude Code · Codex CLI · Aider · Cline

+
+

The agent runs on your machine, edits your local checkout, runs local commands. Excellent at code changes. Anything beyond your machine — databases, networking, deploy targets, runtime logs — is yours to wire in. Install the ZCP MCP locally and run `zcli vpn up`, and any of these agents can now reach a real Zerops project alongside your local code: services addressed by hostname, deploys, logs, events.

+
+ +
+
+

Code-execution sandboxes

+
+ E2B + Modal + Daytona + CodeSandbox SDK +
+

E2B · Modal · Daytona · CodeSandbox SDK

+
+

Ephemeral Linux environments that AI *applications* spin up — often per request — to execute generated code safely. The right primitive when you're building a product that needs a model to run untrusted code and hand back the output. They're a building block for AI applications, not somewhere a coding agent settles in to iterate on your project. Different shape from ZCP; they show up here because the names cluster nearby.

+
+ +
+
+

Agent SDKs and frameworks

+
+ Cloudflare Agents + Vercel AI SDK + Mastra + Anthropic Agent SDK +
+

Cloudflare Agents · Vercel AI SDK · Mastra · Anthropic Agent SDK

+
+

Libraries for building and shipping agents to *your* users — tool calling, memory, orchestration, model providers, sometimes hosting. They're how somebody would build a product like ZCP, not where a coding agent runs against a project. Same disambiguation — adjacent on the shelf, different shape.

+
+ +

## Where to start diff --git a/apps/docs/content/features/local-remote-development.mdx b/apps/docs/content/features/local-remote-development.mdx index 0f24185a4..af7cc8269 100644 --- a/apps/docs/content/features/local-remote-development.mdx +++ b/apps/docs/content/features/local-remote-development.mdx @@ -5,9 +5,7 @@ description: Three ways to develop on Zerops — local with VPN, Cloud IDE, or y import Icons from '@theme/Icon'; -Zerops development is network-first. Keep your editor on your laptop, work fully inside Zerops, or use a native editor over SSH; in each case you join the same project-private network and use the same hostnames, credentials, managed services, and deploy pipeline. - -Development, staging, and production can use the same Zerops-managed service types, networking model, and `zerops.yaml` patterns. What changes is environment policy: credentials, resource allocation, access rules, data, and release authority. +Zerops development is network-first. Keep your editor on your laptop, work fully inside Zerops, or use a native editor over SSH — in each case you join the same project-private network with the same hostnames, credentials, managed services, and deploy pipeline that staging and production use. What changes between environments is policy: credentials, resources, access rules, data, and release authority — not the platform underneath. ## How development on Zerops works @@ -19,7 +17,21 @@ The development question is how you get onto that network. Three paths give you - **Cloud IDE.** A Linux workspace 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` into a workspace or directly into a runtime service container. -Same `db:5432`, same `api:3000`, same project credentials. Switch paths without changing the development project. +Switch paths without reprovisioning the project. A teammate on Local + VPN and another in the Cloud IDE are hitting the same `db:5432`. + +{` Laptop + editor Browser tab Laptop + native IDE + │ │ │ + `}{`zcli vpn up`}{` `}{`VS Code Server`}{` `}{`zcli vpn up`}{` + (WireGuard) on the project + `}{`ssh apidev`}{` + │ │ │ + └──────────────────────┴────────────────────────┘ + │ + ▼ + ┌─────────── project private network ───────────┐ + │ │ + │ `}{`api:3000`}{` `}{`db:5432`}{` `}{`cache:6379`}{` │ + │ │ + └───────────────────────────────────────────────┘`} ## Local + VPN @@ -103,28 +115,18 @@ See the [SSH reference guide](/references/networking/ssh). | --------------------- | ------------------ | -------------------------- | --------------------------------- | | Editor runs | Local | Browser | Local | | Toolchain | Local | Hosted workspace | Hosted workspace or service container | -| Dev databases | Hostname via VPN | Native, on private network | Native, on private network | +| Network entry | WireGuard VPN | Browser into the project | WireGuard VPN, then SSH | | 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 one path. The same project can support all three without reprovisioning. A teammate on Local + VPN and another in Cloud IDE are hitting the same `db:5432`. - -## Same platform model from development to production - -The environments above do not approximate production by inventing a different platform. Development, staging, and production can run the same Zerops-managed service types, private networking model, load-balancer model, and `zerops.yaml` patterns. The remaining differences are scale, credentials, access policy, data, and release authority. - -That makes the "but it works on my machine" failure mode harder to reach. Your dev Postgres, queue, cache, or object storage can be the same managed service type the app meets later in stage or production. - -## How this differs from cloud IDEs +## Why this is not just a cloud IDE If you've used GitHub Codespaces or Gitpod, Cloud IDE looks similar: a Linux container in the cloud, accessed from a browser or local IDE. Two structural differences matter. -**You don't have to be in the editor.** Codespaces and Gitpod are editor-first. Zerops is network-first. Cloud IDE is one way to reach the project's private network; `zcli vpn up` lets you skip the editor environment and consume managed services from your existing local setup. - -**Development, staging, and production use the same platform model.** On a CDE (cloud development environment), your dev environment is one platform and your deployment target is another. On Zerops, managed services, private networking, and `zerops.yaml` patterns work the same way. Environment policy still differs: credentials, resources, access rules, data, and release process. +**You don't have to be in the editor.** Codespaces and Gitpod are editor-first. Zerops is network-first. Cloud IDE is one way to reach the project's private network; `zcli vpn up` lets you skip the editor environment entirely and consume the project's managed services from your existing local setup. The hosted workspace itself is just another Zerops service — SSH in, install packages, run processes, configure it however you want. -A hosted workspace is just a Zerops service. 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. +**Your dev environment runs the same platform as production.** On a CDE, your dev container is one platform and the deployment target is another — making "works on my machine" a fact of life. On Zerops, dev Postgres, queue, cache, or object storage is the same managed service type the app meets later in stage and prod, on the same private network, with the same `zerops.yaml`. Policy differs across environments; the platform underneath doesn't. ## Where to start @@ -132,4 +134,5 @@ A hosted workspace is just a Zerops service. You can SSH into it, install your o { type: 'link', href: '/references/networking/vpn', label: 'VPN reference', customProps: { icon: Icons['link'], description: 'Connect your laptop to a Zerops project-private network.' } }, { type: 'link', href: '/references/networking/ssh', label: 'SSH reference', customProps: { icon: Icons['command-line'], description: 'Use a native editor or shell over SSH.' } }, { type: 'link', href: '/features/pipeline', label: 'Build & deploy pipeline', customProps: { icon: Icons['circle-stack'], description: 'Build and deploy from the same project model.' } }, + { type: 'link', href: '/features/coding-agents', label: 'Infrastructure for Coding Agents', customProps: { icon: Icons['sparkles'], description: 'The sibling feature: agents working on Zerops projects through the same access model.' } }, ]} /> diff --git a/apps/docs/package.json b/apps/docs/package.json index c64f10988..6863e4e89 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -16,7 +16,8 @@ "write-heading-ids": "docusaurus write-heading-ids", "lint": "eslint --ext .js,.jsx,.ts,.tsx . --fix", "lint:content": "eslint --no-eslintrc -c .content.eslintrc.js content --fix", - "diagram2code:generate": "docusaurus diagram2code:generate" + "diagram2code:generate": "docusaurus diagram2code:generate", + "test": "node --test src/plugins/markdown-source/__tests__/*.test.js" }, "dependencies": { "@babel/preset-react": "^7.18.6", diff --git a/apps/docs/src/components/AsciiGraph/index.tsx b/apps/docs/src/components/AsciiGraph/index.tsx new file mode 100644 index 000000000..33d336e05 --- /dev/null +++ b/apps/docs/src/components/AsciiGraph/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; + +type AsciiGraphProps = { + children: React.ReactNode; + className?: string; + ariaLabel?: string; +}; + +const AsciiGraph: React.FC = ({ + children, + className, + ariaLabel, +}) => { + return ( +
+      {children}
+    
+ ); +}; + +export default AsciiGraph; diff --git a/apps/docs/src/components/CodingAgentsTopology/build.cjs b/apps/docs/src/components/CodingAgentsTopology/build.cjs new file mode 100644 index 000000000..2733b03eb --- /dev/null +++ b/apps/docs/src/components/CodingAgentsTopology/build.cjs @@ -0,0 +1,168 @@ +'use strict'; + +// Build functions for the ZCP topology diagram. Pure functions that produce +// a Grid (from grid.cjs). Consumed by: +// - the React component (index.tsx) for the interactive Remote/Local toggle +// - the markdown-source plugin to render the same ASCII into .md output + +const { + make, + text, + textCenter, + vline, + hline, + ch, + joint, + box, + verify, +} = require('./grid.cjs'); + +const W = 80; +const PROJECT_W = 62; + +function buildProject(g, mode) { + const yTop = 3; + const yBottom = 31; + + box(g, { x: 0, y: yTop, w: PROJECT_W, h: yBottom - yTop + 1 }); + textCenter(g, 31, yTop + 1, 'Zerops development project (private network)'); + + box(g, { + x: 2, y: 5, w: 19, h: 5, + lines: ['project ctrl', '+ L3 balancer', '+ firewall'], + }); + box(g, { x: 23, y: 5, w: 12, h: 5, lines: ['stats'], align: 'center' }); + box(g, { x: 37, y: 5, w: 12, h: 5, lines: ['logger'], align: 'center' }); + + box(g, { + x: 2, y: 11, w: 19, h: 4, + lines: ['L7 balancer', '(Nginx)'], + align: 'center', + }); + + joint(g, 11, 9, '┬'); + vline(g, 11, 10, 10); + joint(g, 11, 11, '┴'); + + joint(g, 11, 14, '┬'); + vline(g, 11, 15, 15); + joint(g, 11, 16, '├'); + + if (mode === 'remote') { + hline(g, 16, 12, 46); + joint(g, 29, 16, '┬'); + joint(g, 47, 16, '┐'); + } else { + hline(g, 16, 12, 28); + joint(g, 29, 16, '┐'); + } + + box(g, { + x: 4, y: 17, w: 15, h: 4, + lines: ['appdev:3000', 'dev runtime'], + }); + joint(g, 11, 17, '┴'); + joint(g, 11, 20, '┬'); + vline(g, 11, 21, 21); + + box(g, { + x: 21, y: 17, w: 17, h: 4, + lines: ['appstage:3000', 'stage runtime'], + }); + joint(g, 29, 17, '┴'); + joint(g, 29, 20, '┬'); + vline(g, 29, 21, 21); + + if (mode === 'remote') { + box(g, { + x: 40, y: 17, w: 15, h: 4, + lines: ['zcp service', 'agent + MCP'], + align: 'center', + hl: true, + }); + joint(g, 47, 17, '┴'); + joint(g, 47, 20, '┬'); + vline(g, 47, 21, 21); + textCenter(g, 47, 13, 'control plane'); + textCenter(g, 47, 14, 'ssh into other services'); + textCenter(g, 47, 15, 'dev runtime fs mounted'); + } + + joint(g, 11, 22, '└'); + if (mode === 'remote') { + hline(g, 22, 12, 46); + joint(g, 29, 22, '┼'); + joint(g, 47, 22, '┘'); + } else { + hline(g, 22, 12, 28); + joint(g, 20, 22, '┬'); + joint(g, 29, 22, '┘'); + } + + const downCol = mode === 'remote' ? 29 : 20; + vline(g, downCol, 23, 23); + + joint(g, 14, 24, '┌'); + hline(g, 24, 15, 41); + joint(g, downCol, 24, '┴'); + joint(g, 42, 24, '┐'); + + vline(g, 14, 25, 25); + vline(g, 42, 25, 25); + + box(g, { x: 7, y: 26, w: 15, h: 4, lines: ['db:5432', 'Postgres'] }); + joint(g, 14, 26, '┴'); + + box(g, { x: 35, y: 26, w: 15, h: 4, lines: ['cache:6379', 'Valkey'] }); + joint(g, 42, 26, '┴'); +} + +function buildRemote() { + const g = make(W, 32); + textCenter(g, 31, 0, 'https://my-app.com'); + vline(g, 31, 1, 1); + ch(g, 31, 2, '▼'); + buildProject(g, 'remote'); + verify(g); + return g; +} + +function buildLocal() { + const g = make(W, 44); + textCenter(g, 31, 0, 'https://my-app.com'); + vline(g, 31, 1, 1); + ch(g, 31, 2, '▼'); + buildProject(g, 'local'); + + joint(g, 31, 31, '▲'); + + vline(g, 31, 32, 34); + text(g, 33, 33, 'VPN tunnel'); + + box(g, { + x: 14, y: 35, w: 35, h: 9, + lines: [ + 'IDE / terminal', + '+ agent', + '+ zcp MCP', + '', + '$ curl zerops.io/zcp/install.sh', + '$ zcp init', + '$ zcli vpn up', + ], + align: 'left', + hl: true, + }); + joint(g, 31, 35, '┴'); + + verify(g); + return g; +} + +module.exports = { + buildProject, + buildRemote, + buildLocal, + W, + PROJECT_W, +}; diff --git a/apps/docs/src/components/CodingAgentsTopology/grid.cjs b/apps/docs/src/components/CodingAgentsTopology/grid.cjs new file mode 100644 index 000000000..ccc5eebdb --- /dev/null +++ b/apps/docs/src/components/CodingAgentsTopology/grid.cjs @@ -0,0 +1,149 @@ +'use strict'; + +// Pure grid utilities — used by the React component (CodingAgentsTopology, +// IntroAgentVisual) and by the markdown-source plugin to render the same +// ASCII art as plain text into the .md output. +// +// Mirrors the API of the original grid.tsx but returns plain strings rather +// than React nodes from `render`. The .tsx wrapper (`grid.tsx`) delegates +// here for the grid math and re-implements `render` to emit React nodes +// with `` highlights. + +const SPACE = ' '; + +function make(width, height) { + return Array.from({ length: height }, () => + Array.from({ length: width }, () => ({ ch: SPACE, hl: false })) + ); +} + +function put(g, x, y, ch, hl) { + if (y < 0 || y >= g.length) return; + if (x < 0 || x >= g[0].length) return; + g[y][x] = { ch, hl }; +} + +function text(g, x, y, str, hl = false) { + for (let i = 0; i < str.length; i++) put(g, x + i, y, str[i], hl); +} + +function textCenter(g, cx, y, str, hl = false) { + text(g, cx - Math.floor(str.length / 2), y, str, hl); +} + +function vline(g, x, y1, y2, hl = false) { + const [a, b] = y1 <= y2 ? [y1, y2] : [y2, y1]; + for (let y = a; y <= b; y++) put(g, x, y, '│', hl); +} + +function hline(g, y, x1, x2, hl = false) { + const [a, b] = x1 <= x2 ? [x1, x2] : [x2, x1]; + for (let x = a; x <= b; x++) put(g, x, y, '─', hl); +} + +function ch(g, x, y, char, hl = false) { + put(g, x, y, char, hl); +} + +function joint(g, x, y, char) { + if (y < 0 || y >= g.length || x < 0 || x >= g[0].length) return; + g[y][x] = { ch: char, hl: g[y][x].hl }; +} + +function box(g, o) { + const { x, y, w, h, lines = [], align = 'left', topLabel, hl = false, subtitle = false } = o; + if (hl) { + for (let yy = y + 1; yy < y + h - 1; yy++) { + for (let xx = x + 1; xx < x + w - 1; xx++) { + put(g, xx, yy, SPACE, true); + } + } + } + put(g, x, y, '┌', hl); + put(g, x + w - 1, y, '┐', hl); + put(g, x, y + h - 1, '└', hl); + put(g, x + w - 1, y + h - 1, '┘', hl); + for (let i = 1; i < w - 1; i++) { + put(g, x + i, y, '─', hl); + put(g, x + i, y + h - 1, '─', hl); + } + for (let i = 1; i < h - 1; i++) { + put(g, x, y + i, '│', hl); + put(g, x + w - 1, y + i, '│', hl); + } + if (topLabel) { + const label = ` ${topLabel} `; + for (let i = 0; i < label.length && 2 + i < w - 2; i++) { + put(g, x + 2 + i, y, label[i], hl); + } + } + const inner = w - 2; + for (let i = 0; i < lines.length; i++) { + if (1 + i >= h - 1) break; + const line = lines[i]; + const startCol = + align === 'center' + ? x + 1 + Math.floor((inner - line.length) / 2) + : x + 2; + const accent = subtitle && i > 0; + for (let j = 0; j < line.length; j++) { + if (startCol + j >= x + w - 1) break; + const yy = y + 1 + i; + const xx = startCol + j; + if (yy < 0 || yy >= g.length || xx < 0 || xx >= g[0].length) continue; + g[yy][xx] = { ch: line[j], hl, accent }; + } + } +} + +function highlight(g, x, y, w, h) { + for (let yy = y; yy < y + h; yy++) { + for (let xx = x; xx < x + w; xx++) { + if (yy < 0 || yy >= g.length || xx < 0 || xx >= g[0].length) continue; + g[yy][xx] = { ch: g[yy][xx].ch, hl: true }; + } + } +} + +function verify(g) { + const width = g[0]?.length ?? 0; + for (const row of g) { + if (row.length !== width) throw new Error('Grid rows have inconsistent width'); + } + return { width, height: g.length }; +} + +// Plain-text rendering for the markdown plugin. Drops React span wrappers +// and emits trailing-whitespace-trimmed lines joined with '\n'. +function renderText(g) { + const lines = []; + for (const row of g) { + let end = row.length; + while ( + end > 0 && + row[end - 1].ch === SPACE && + !row[end - 1].hl && + !row[end - 1].accent + ) { + end--; + } + let line = ''; + for (let i = 0; i < end; i++) line += row[i].ch; + lines.push(line); + } + return lines.join('\n'); +} + +module.exports = { + make, + text, + textCenter, + vline, + hline, + ch, + joint, + box, + highlight, + verify, + renderText, +}; diff --git a/apps/docs/src/components/CodingAgentsTopology/grid.tsx b/apps/docs/src/components/CodingAgentsTopology/grid.tsx new file mode 100644 index 000000000..51c813f61 --- /dev/null +++ b/apps/docs/src/components/CodingAgentsTopology/grid.tsx @@ -0,0 +1,83 @@ +// Re-exports the pure grid utilities from grid.cjs and adds a React-flavored +// `render` that wraps highlighted/accent runs in elements. The .cjs +// module is shared with the markdown-source plugin so the same ASCII art +// renders in both the live page and the .md output. +// +// `Grid` is intentionally typed loosely (`any[][]`) because the cell shape +// comes from grid.cjs. Strong typing isn't worth a parallel .d.ts file when +// the consuming React component only passes the grid through. + +import React from 'react'; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const grid = require('./grid.cjs'); + +export type Grid = any[][]; + +const SPACE = ' '; + +export const make: (width: number, height: number) => Grid = grid.make; +export const text: (g: Grid, x: number, y: number, str: string, hl?: boolean) => void = grid.text; +export const textCenter: (g: Grid, cx: number, y: number, str: string, hl?: boolean) => void = grid.textCenter; +export const vline: (g: Grid, x: number, y1: number, y2: number, hl?: boolean) => void = grid.vline; +export const hline: (g: Grid, y: number, x1: number, x2: number, hl?: boolean) => void = grid.hline; +export const ch: (g: Grid, x: number, y: number, char: string, hl?: boolean) => void = grid.ch; +export const joint: (g: Grid, x: number, y: number, char: string) => void = grid.joint; + +type BoxOpts = { + x: number; + y: number; + w: number; + h: number; + lines?: string[]; + align?: 'left' | 'center'; + topLabel?: string; + hl?: boolean; + subtitle?: boolean; +}; +export const box: (g: Grid, o: BoxOpts) => void = grid.box; +export const highlight: (g: Grid, x: number, y: number, w: number, h: number) => void = grid.highlight; +export const verify: (g: Grid) => { width: number; height: number } = grid.verify; + +export function render(g: Grid): React.ReactNode[] { + const out: React.ReactNode[] = []; + for (let y = 0; y < g.length; y++) { + if (y > 0) out.push('\n'); + const row = g[y]; + let end = row.length; + while ( + end > 0 && + row[end - 1].ch === SPACE && + !row[end - 1].hl && + !row[end - 1].accent + ) + end--; + let i = 0; + while (i < end) { + const hl = row[i].hl; + const accent = !!row[i].accent; + let j = i; + while (j < end && row[j].hl === hl && !!row[j].accent === accent) j++; + const seg = row + .slice(i, j) + .map((c: { ch: string }) => c.ch) + .join(''); + if (hl) { + out.push( + + {seg} + + ); + } else if (accent) { + out.push( + + {seg} + + ); + } else { + out.push(seg); + } + i = j; + } + } + return out; +} diff --git a/apps/docs/src/components/CodingAgentsTopology/index.tsx b/apps/docs/src/components/CodingAgentsTopology/index.tsx new file mode 100644 index 000000000..4ab233b6b --- /dev/null +++ b/apps/docs/src/components/CodingAgentsTopology/index.tsx @@ -0,0 +1,53 @@ +import React, { useState } from 'react'; +import { render } from './grid'; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { buildRemote, buildLocal } = require('./build.cjs'); + +type Mode = 'remote' | 'local'; + + +const CodingAgentsTopology: React.FC = () => { + const [mode, setMode] = useState('remote'); + const diagram = mode === 'remote' ? buildRemote() : buildLocal(); + + return ( +
+
+ + +
+
+        {render(diagram)}
+      
+
+ ); +}; + +export default CodingAgentsTopology; diff --git a/apps/docs/src/components/IntroAgentVisual/build.cjs b/apps/docs/src/components/IntroAgentVisual/build.cjs new file mode 100644 index 000000000..09690850c --- /dev/null +++ b/apps/docs/src/components/IntroAgentVisual/build.cjs @@ -0,0 +1,136 @@ +'use strict'; + +// Build function for the agents-to-recipes-to-projects intro visual. +// Pure JS, no React. Consumed by: +// - the React component (index.tsx) for the live page (renders logo images +// absolutely positioned over the ASCII grid) +// - the markdown-source plugin to render the same diagram as plain text +// (logos rendered as their alt text overlaid into the grid) + +const { + make, + text, + textCenter, + vline, + hline, + ch, + joint, + box, + verify, +} = require('../CodingAgentsTopology/grid.cjs'); + +const W = 86; + +const AGENTS = [ + { src: '/img/recipes/claude.png', alt: 'Claude Code' }, + { src: '/img/recipes/codex.svg', alt: 'Codex' }, + { src: '/img/recipes/gemini.svg', alt: 'Gemini CLI' }, + { src: '/img/recipes/opencode.png', alt: 'opencode' }, +]; + +const RECIPES = [ + { src: '/img/recipes/zerops/bun.svg', alt: 'Bun' }, + { src: '/img/recipes/zerops/deno.svg', alt: 'Deno' }, + { src: '/img/recipes/zerops/nodejs.svg', alt: 'Node.js' }, + { src: '/img/recipes/zerops/golang.svg', alt: 'Go' }, + { src: '/img/recipes/zerops/java.svg', alt: 'Java' }, + { src: '/img/recipes/zerops/python.svg', alt: 'Python' }, + { src: '/img/recipes/zerops/rust.svg', alt: 'Rust' }, + { src: '/img/recipes/zerops/gleam.svg', alt: 'Gleam' }, + { src: '/img/recipes/zerops/ruby.svg', alt: 'Ruby' }, + { src: '/img/recipes/zerops/php.svg', alt: 'PHP' }, + { src: '/img/recipes/zerops/dotnet.svg', alt: '.NET' }, + { src: '/img/recipes/zerops/laravel.svg', alt: 'Laravel' }, + { src: '/img/recipes/zerops/nextjs.svg', alt: 'Next.js' }, + { src: '/img/recipes/zerops/nuxt.svg', alt: 'Nuxt' }, + { src: '/img/recipes/zerops/astro.svg', alt: 'Astro' }, + { src: '/img/recipes/zerops/svelte.svg', alt: 'Svelte' }, + { src: '/img/recipes/zerops/react.svg', alt: 'React' }, + { src: '/img/recipes/zerops/vue.svg', alt: 'Vue' }, + { src: '/img/recipes/zerops/angular.png', alt: 'Angular' }, + { src: '/img/recipes/zerops/solid.svg', alt: 'Solid' }, + { src: '/img/recipes/zerops/qwik.svg', alt: 'Qwik' }, + { src: '/img/recipes/zerops/analog.svg', alt: 'Analog' }, + { src: '/img/recipes/zerops/nestjs.svg', alt: 'Nest.js' }, +]; + +function buildLayout() { + const g = make(W, 40); + const logos = []; + + box(g, { x: 2, y: 0, w: 16, h: 8, topLabel: 'ZCP MCP' }); + const AGENT_POS = [ + [6, 2], [13, 2], + [6, 5], [13, 5], + ]; + AGENTS.forEach((a, i) => { + const [col, row] = AGENT_POS[i]; + logos.push({ src: a.src, alt: a.alt, col, row }); + }); + + box(g, { x: 22, y: 0, w: 58, h: 11, topLabel: 'recipes for any stack' }); + const RECIPE_COLS = [26, 33, 40, 47, 54, 61, 68, 75]; + RECIPES.forEach((r, i) => { + const rowIdx = Math.floor(i / 8); + const colIdx = i % 8; + const row = 2 + rowIdx * 3; + logos.push({ src: r.src, alt: r.alt, col: RECIPE_COLS[colIdx], row }); + }); + + joint(g, 17, 4, '├'); + hline(g, 4, 18, 20); + ch(g, 21, 4, '►'); + + textCenter(g, 42, 12, 'zcp with agent has root ssh access to runtime services,'); + textCenter(g, 42, 13, 'runs dev server on dev, deploys to stage, verifies'); + + joint(g, 10, 7, '┬'); + vline(g, 10, 8, 14); + joint(g, 10, 15, '├'); + hline(g, 15, 11, 73); + joint(g, 74, 15, '┐'); + + vline(g, 10, 16, 16); + joint(g, 10, 17, '┼'); + vline(g, 10, 18, 18); + ch(g, 10, 19, '▼'); + + vline(g, 74, 16, 16); + joint(g, 74, 17, '┼'); + vline(g, 74, 18, 18); + ch(g, 74, 19, '▼'); + + box(g, { x: 0, y: 17, w: 60, h: 22, topLabel: 'complex project' }); + + box(g, { x: 3, y: 20, w: 14, h: 3, lines: ['zcp'], align: 'center', hl: true }); + + box(g, { x: 3, y: 24, w: 14, h: 4, lines: ['appdev', 'Bun'], align: 'center', subtitle: true }); + box(g, { x: 23, y: 24, w: 14, h: 4, lines: ['apidev', 'Golang'], align: 'center', subtitle: true }); + box(g, { x: 43, y: 24, w: 14, h: 4, lines: ['workerdev', 'Python'], align: 'center', subtitle: true }); + + box(g, { x: 3, y: 29, w: 14, h: 4, lines: ['appstage', 'Bun'], align: 'center', subtitle: true }); + box(g, { x: 23, y: 29, w: 14, h: 4, lines: ['apistage', 'Golang'], align: 'center', subtitle: true }); + box(g, { x: 43, y: 29, w: 14, h: 4, lines: ['workerstage', 'Python'], align: 'center', subtitle: true }); + + box(g, { x: 3, y: 34, w: 10, h: 4, lines: ['db', 'Postgres'], align: 'center', subtitle: true }); + box(g, { x: 14, y: 34, w: 10, h: 4, lines: ['search', 'Elastic'], align: 'center', subtitle: true }); + box(g, { x: 25, y: 34, w: 10, h: 4, lines: ['broker', 'NATS'], align: 'center', subtitle: true }); + box(g, { x: 36, y: 34, w: 10, h: 4, lines: ['cache', 'Valkey'], align: 'center', subtitle: true }); + box(g, { x: 47, y: 34, w: 10, h: 4, lines: ['storage', 'S3'], align: 'center', subtitle: true }); + + box(g, { x: 64, y: 17, w: 22, h: 17, topLabel: 'simple project' }); + + box(g, { x: 69, y: 20, w: 12, h: 3, lines: ['zcp'], align: 'center', hl: true }); + box(g, { x: 69, y: 24, w: 12, h: 4, lines: ['appdev', 'Node.js'], align: 'center', subtitle: true }); + box(g, { x: 69, y: 29, w: 12, h: 4, lines: ['appstage', 'Node.js'], align: 'center', subtitle: true }); + + verify(g); + return { grid: g, logos }; +} + +module.exports = { + buildLayout, + AGENTS, + RECIPES, + W, +}; diff --git a/apps/docs/src/components/IntroAgentVisual/index.tsx b/apps/docs/src/components/IntroAgentVisual/index.tsx new file mode 100644 index 000000000..08c01a917 --- /dev/null +++ b/apps/docs/src/components/IntroAgentVisual/index.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { render } from '../CodingAgentsTopology/grid'; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { buildLayout } = require('./build.cjs'); + +type LogoSlot = { src: string; alt: string; col: number; row: number }; + +const IntroAgentVisual: React.FC = () => { + const { grid, logos } = buildLayout() as { + grid: any[][]; + logos: LogoSlot[]; + }; + + return ( +
+
{render(grid)}
+ {logos.map((l, i) => ( + {l.alt} + ))} +
+ ); +}; + +export default IntroAgentVisual; diff --git a/apps/docs/src/css/custom.css b/apps/docs/src/css/custom.css index 5c42fb2eb..8b3bc0a87 100644 --- a/apps/docs/src/css/custom.css +++ b/apps/docs/src/css/custom.css @@ -632,3 +632,765 @@ html[data-theme='dark'] .zcp-lifecycle-stage--delivery { transform: translateY(-50%); } } + +/* === coding-agents feature page: claim grid, gap cards, comparison cards, pull quote, hack panel, run-split === */ + +.zcp-claim-grid { + display: grid; + gap: 0.95rem; + grid-template-columns: 1fr; + margin: 1.25rem 0 2rem; +} + +.zcp-claim-card, +.zcp-claim-callout { + background: #ffffff; + border: 1px solid #d7dce5; + border-radius: 8px; + color: #172033; + padding: 1rem; +} + +.zcp-claim-card span, +.zcp-claim-callout span { + color: #6b7280; + display: block; + font-size: 0.68rem; + font-weight: 650; + letter-spacing: 0; + line-height: 1.1; + margin-bottom: 0.45rem; + text-transform: uppercase; +} + +.zcp-claim-card h3, +.zcp-claim-callout h3 { + font-size: 1rem; + line-height: 1.25; + margin: 0; +} + +.zcp-claim-card p, +.zcp-claim-callout p { + color: #4b5563; + font-size: 0.84rem; + line-height: 1.5; + margin: 0.55rem 0 0; +} + +.zcp-claim-card--services { background: #f4fbf6; border-color: rgba(50, 132, 90, 0.35); } +.zcp-claim-card--deploy { background: #f7f5ff; border-color: rgba(113, 87, 217, 0.35); } +.zcp-claim-card--stack { background: #f7fbff; border-color: rgba(45, 114, 217, 0.35); } +.zcp-claim-card--handover { background: #fffaf0; border-color: rgba(196, 127, 23, 0.35); } + +.zcp-claim-callout { + background: #172033; + border-color: #172033; + color: #ffffff; +} +.zcp-claim-callout span, +.zcp-claim-callout p { color: #d1d5db; } +.zcp-claim-callout h3 { color: #ffffff; } + +@media (min-width: 768px) { + .zcp-claim-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .zcp-claim-callout { grid-column: 1 / -1; padding: 1.15rem 1.25rem; } +} + +html[data-theme='dark'] .zcp-claim-card { color: #f8fafc; } +html[data-theme='dark'] .zcp-claim-card p, +html[data-theme='dark'] .zcp-claim-card span { color: #cbd5e1; } +html[data-theme='dark'] .zcp-claim-card--services { background: rgba(20, 83, 45, 0.22); } +html[data-theme='dark'] .zcp-claim-card--deploy { background: rgba(76, 29, 149, 0.22); } +html[data-theme='dark'] .zcp-claim-card--stack { background: rgba(30, 58, 138, 0.22); } +html[data-theme='dark'] .zcp-claim-card--handover { background: rgba(120, 53, 15, 0.2); } + +/* gap objection cards */ + +.zcp-gap-grid { + display: grid; + gap: 0.8rem; + grid-template-columns: 1fr; + margin: 1rem 0 1.75rem; +} + +.zcp-gap-card { + background: #fffaf0; + border: 1px solid rgba(196, 127, 23, 0.35); + border-radius: 8px; + padding: 0.9rem; +} + +.zcp-gap-card h3 { color: #172033; font-size: 0.95rem; line-height: 1.25; margin: 0; } +.zcp-gap-card p { color: #4b5563; font-size: 0.82rem; line-height: 1.45; margin: 0.45rem 0 0; } + +@media (min-width: 768px) { + .zcp-gap-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } +} + +html[data-theme='dark'] .zcp-gap-card { background: rgba(120, 53, 15, 0.2); } +html[data-theme='dark'] .zcp-gap-card h3 { color: #f8fafc; } +html[data-theme='dark'] .zcp-gap-card p { color: #cbd5e1; } + +/* competitor comparison cards + highlighted summary table */ + +.zcp-compare-grid { + display: grid; + gap: 0.9rem; + grid-template-columns: 1fr; + margin: 1.2rem 0 1.5rem; +} + +.zcp-compare-card { + background: #ffffff; + border: 1px solid #d7dce5; + border-radius: 8px; + padding: 0.95rem; +} + +.zcp-compare-card span { color: #6b7280; display: block; font-size: 0.68rem; font-weight: 650; margin-bottom: 0.4rem; text-transform: uppercase; } +.zcp-compare-card h3 { font-size: 0.98rem; line-height: 1.25; margin: 0; } +.zcp-compare-card p { color: #4b5563; font-size: 0.82rem; line-height: 1.48; margin: 0.5rem 0 0.7rem; } +.zcp-compare-card strong { color: #236343; display: block; font-size: 0.8rem; line-height: 1.35; } + +.zcp-summary-table table tr:last-child { background: #f4fbf6; } +.zcp-summary-table table tr:last-child td { border-top: 1px solid rgba(50, 132, 90, 0.35); font-weight: 600; } + +@media (min-width: 768px) { + .zcp-compare-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .zcp-compare-card--wide { grid-column: 1 / -1; } +} + +html[data-theme='dark'] .zcp-compare-card { background: rgba(24, 24, 27, 0.72); color: #f8fafc; } +html[data-theme='dark'] .zcp-compare-card p, +html[data-theme='dark'] .zcp-compare-card span { color: #cbd5e1; } +html[data-theme='dark'] .zcp-compare-card strong { color: #86efac; } +html[data-theme='dark'] .zcp-summary-table table tr:last-child { background: rgba(20, 83, 45, 0.22); } + +/* project boundary pull quote */ + +.zcp-boundary-quote { + background: #f7fbff; + border: 1px solid rgba(45, 114, 217, 0.35); + border-left: 4px solid #2d72d9; + border-radius: 8px; + margin: 1.25rem 0; + padding: 1rem 1.15rem; +} + +.zcp-boundary-quote p { + color: #172033; + font-size: 1.05rem; + font-weight: 650; + line-height: 1.45; + margin: 0; +} + +html[data-theme='dark'] .zcp-boundary-quote { background: rgba(30, 58, 138, 0.22); } +html[data-theme='dark'] .zcp-boundary-quote p { color: #f8fafc; } + +/* hack-on-the-workspace panel */ + +.zcp-hack-panel { + background: #f8fafc; + border: 1px solid rgba(75, 85, 99, 0.28); + border-radius: 8px; + margin: 1rem 0 1.5rem; + padding: 1rem; +} + +.zcp-hack-panel > div span { color: #6b7280; display: block; font-size: 0.68rem; font-weight: 650; margin-bottom: 0.35rem; text-transform: uppercase; } +.zcp-hack-panel > div strong { color: #172033; display: block; font-size: 1rem; line-height: 1.25; } +.zcp-hack-panel ul { display: grid; gap: 0.45rem; list-style: none; margin: 0.8rem 0 0; padding: 0; } +.zcp-hack-panel li { background: #ffffff; border: 1px solid #d7dce5; border-radius: 6px; color: #4b5563; font-size: 0.82rem; line-height: 1.4; padding: 0.5rem 0.6rem; } + +@media (min-width: 768px) { + .zcp-hack-panel { display: grid; gap: 1rem; grid-template-columns: minmax(0, 0.75fr) minmax(0, 1.25fr); } + .zcp-hack-panel ul { margin-top: 0; } +} + +html[data-theme='dark'] .zcp-hack-panel { background: rgba(24, 24, 27, 0.72); } +html[data-theme='dark'] .zcp-hack-panel > div strong { color: #f8fafc; } +html[data-theme='dark'] .zcp-hack-panel li { background: rgba(15, 23, 42, 0.55); color: #cbd5e1; } + +/* where it runs: remote/local split */ + +.zcp-run-split { display: grid; gap: 0.85rem; grid-template-columns: 1fr; margin: 1rem 0 1.4rem; } +.zcp-run-split article { background: #f7fbff; border: 1px solid rgba(45, 114, 217, 0.35); border-radius: 8px; padding: 0.95rem; } +.zcp-run-split span { color: #6b7280; display: block; font-size: 0.68rem; font-weight: 650; margin-bottom: 0.4rem; text-transform: uppercase; } +.zcp-run-split h3 { color: #172033; font-size: 0.98rem; line-height: 1.25; margin: 0; } +.zcp-run-split p { color: #4b5563; font-size: 0.82rem; line-height: 1.45; margin: 0.5rem 0 0; } + +@media (min-width: 768px) { + .zcp-run-split { grid-template-columns: repeat(2, minmax(0, 1fr)); } +} + +html[data-theme='dark'] .zcp-run-split article { background: rgba(30, 58, 138, 0.22); } +html[data-theme='dark'] .zcp-run-split h3 { color: #f8fafc; } +html[data-theme='dark'] .zcp-run-split p, +html[data-theme='dark'] .zcp-run-split span { color: #cbd5e1; } + +/* substrate cards: structural differentiators */ + +.zcp-substrate-grid { + display: grid; + gap: 0.9rem; + grid-template-columns: 1fr; + margin: 1.1rem 0 1.85rem; +} + +.zcp-substrate-card { + background: #f1f5f9; + border: 1px solid rgba(45, 65, 96, 0.28); + border-radius: 8px; + padding: 1rem; +} + +.zcp-substrate-card span { + color: #475569; + display: block; + font-size: 0.68rem; + font-weight: 650; + letter-spacing: 0; + line-height: 1.1; + margin-bottom: 0.45rem; + text-transform: uppercase; +} + +.zcp-substrate-card h3 { + color: #172033; + font-size: 1rem; + line-height: 1.25; + margin: 0; +} + +.zcp-substrate-card p { + color: #4b5563; + font-size: 0.84rem; + line-height: 1.5; + margin: 0.55rem 0 0; +} + +.zcp-substrate-card p em { + background: rgba(196, 127, 23, 0.18); + border-radius: 3px; + color: #7c3a02; + font-style: normal; + padding: 0 0.22em; +} + +/* substrate cards stack as rows on all viewports — each claim gets full width + so the reader can connect it back to the topology above */ + +html[data-theme='dark'] .zcp-substrate-card { background: rgba(15, 23, 42, 0.7); } +html[data-theme='dark'] .zcp-substrate-card h3 { color: #f8fafc; } +html[data-theme='dark'] .zcp-substrate-card p, +html[data-theme='dark'] .zcp-substrate-card span { color: #cbd5e1; } +html[data-theme='dark'] .zcp-substrate-card p em { background: rgba(196, 127, 23, 0.25); color: #fde68a; } + +/* framed comparison table for the agent-hosting landscape */ + +.zcp-landscape-table { + background: #ffffff; + border: 1px solid rgba(45, 65, 96, 0.18); + border-radius: 10px; + margin: 1.4rem 0 1.8rem; + overflow: hidden; +} + +.zcp-landscape-table table { + border-collapse: collapse; + display: table; + font-size: 0.88rem; + margin: 0; + width: 100%; +} + +.zcp-landscape-table thead th { + background: #f1f5f9; + border: none; + border-bottom: 1px solid rgba(45, 65, 96, 0.18); + color: #475569; + font-size: 0.7rem; + font-weight: 650; + letter-spacing: 0.05em; + padding: 0.7rem 1rem; + text-align: left; + text-transform: uppercase; +} + +.zcp-landscape-table tbody td { + border: none; + border-bottom: 1px solid rgba(45, 65, 96, 0.08); + color: #4b5563; + line-height: 1.45; + padding: 0.75rem 1rem; + vertical-align: top; +} + +.zcp-landscape-table tbody tr:last-child td { + border-bottom: none; +} + +.zcp-landscape-table tbody td:first-child { + color: #172033; + font-weight: 600; + width: 22%; +} + +.zcp-landscape-table tbody td:last-child { + color: #475569; + font-size: 0.82rem; + width: 26%; +} + +.zcp-landscape-table tbody tr:hover { + background: rgba(45, 65, 96, 0.025); +} + +.zcp-landscape-table__zerops { + background: rgba(45, 114, 217, 0.07); + position: relative; +} + +.zcp-landscape-table__zerops:hover { + background: rgba(45, 114, 217, 0.11) !important; +} + +.zcp-landscape-table__zerops td { + color: #1d4ed8 !important; + font-weight: 600; +} + +.zcp-landscape-table__zerops td:first-child { + box-shadow: inset 3px 0 0 #2d72d9; + color: #1d4ed8 !important; +} + +html[data-theme='dark'] .zcp-landscape-table { + background: rgba(15, 23, 42, 0.55); + border-color: rgba(255, 255, 255, 0.08); +} + +html[data-theme='dark'] .zcp-landscape-table thead th { + background: rgba(255, 255, 255, 0.04); + border-bottom-color: rgba(255, 255, 255, 0.08); + color: #94a3b8; +} + +html[data-theme='dark'] .zcp-landscape-table tbody td { + border-bottom-color: rgba(255, 255, 255, 0.06); + color: #cbd5e1; +} + +html[data-theme='dark'] .zcp-landscape-table tbody td:first-child { + color: #f1f5f9; +} + +html[data-theme='dark'] .zcp-landscape-table tbody td:last-child { + color: #94a3b8; +} + +html[data-theme='dark'] .zcp-landscape-table tbody tr:hover { + background: rgba(255, 255, 255, 0.03); +} + +html[data-theme='dark'] .zcp-landscape-table__zerops { + background: rgba(45, 114, 217, 0.18); +} + +html[data-theme='dark'] .zcp-landscape-table__zerops:hover { + background: rgba(45, 114, 217, 0.24) !important; +} + +html[data-theme='dark'] .zcp-landscape-table__zerops td { + color: #93c5fd !important; +} + +/* agent hosting landscape — plain prose sections with a logo strip and an + explicit "ZCP relationship" line per category */ + +.zcp-landscape-section { + border-top: 1px solid rgba(45, 65, 96, 0.12); + margin: 0; + padding: 1.1rem 0 1.25rem; +} + +.zcp-landscape-section:first-of-type { + border-top: none; + padding-top: 1.5rem; +} + +.zcp-landscape-section header { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.7rem 0.85rem; + margin-bottom: 0.55rem; +} + +.zcp-landscape-section h4 { + color: #172033; + font-size: 1rem; + font-weight: 650; + line-height: 1.25; + margin: 0; +} + +.zcp-landscape-section__logos { + align-items: center; + display: inline-flex; + flex-wrap: wrap; + gap: 0.35rem; +} + +.zcp-landscape-section__logos img { + background: rgba(15, 23, 42, 0.05); + border-radius: 5px; + display: inline-block; + height: 22px; + padding: 3px; + width: 22px; +} + +.zcp-landscape-section__examples { + color: #64748b; + flex-basis: 100%; + font-size: 0.78rem; + margin: 0; + order: 99; +} + +.zcp-landscape-section p { + color: #475569; + font-size: 0.92rem; + line-height: 1.6; + margin: 0 0 0.55rem; +} + +.zcp-landscape-section p:last-child { + margin-bottom: 0; +} + +.zcp-landscape-section__relation { + background: rgba(45, 114, 217, 0.06); + border-left: 3px solid #2d72d9; + border-radius: 0 4px 4px 0; + color: #1e293b !important; + font-size: 0.9rem !important; + margin-top: 0.55rem; + padding: 0.55rem 0.85rem !important; +} + +.zcp-landscape-section__relation strong { + color: #1d4ed8; +} + +.zcp-landscape-section__relation code { + background: rgba(45, 114, 217, 0.12); + border-radius: 3px; + color: #1d4ed8; + font-size: 0.86em; + padding: 0.05em 0.3em; +} + +html[data-theme='dark'] .zcp-landscape-section { + border-top-color: rgba(255, 255, 255, 0.08); +} + +html[data-theme='dark'] .zcp-landscape-section h4 { + color: #f1f5f9; +} + +html[data-theme='dark'] .zcp-landscape-section p { + color: #cbd5e1; +} + +html[data-theme='dark'] .zcp-landscape-section__examples { + color: #94a3b8; +} + +html[data-theme='dark'] .zcp-landscape-section__logos img { + background: rgba(255, 255, 255, 0.08); +} + +html[data-theme='dark'] .zcp-landscape-section__relation { + background: rgba(96, 165, 250, 0.12); + border-left-color: #60a5fa; + color: #e2e8f0 !important; +} + +html[data-theme='dark'] .zcp-landscape-section__relation strong { + color: #93c5fd; +} + +html[data-theme='dark'] .zcp-landscape-section__relation code { + background: rgba(96, 165, 250, 0.2); + color: #bfdbfe; +} + +/* service definition yaml panel */ + +.zcp-yaml-intro { + color: #475569; + font-size: 0.86rem; + line-height: 1.55; + margin: 1rem 0 0.5rem; +} + +.zcp-yaml-intro strong { + color: #172033; + font-weight: 650; +} + +html[data-theme='dark'] .zcp-yaml-intro { color: #cbd5e1; } +html[data-theme='dark'] .zcp-yaml-intro strong { color: #f8fafc; } + +/* unified feature grid (replaces claim cards) */ + +.zcp-features-grid { + display: grid; + gap: 0.7rem; + grid-template-columns: 1fr; + margin: 1.1rem 0 1.85rem; +} + +.zcp-feature-card { + background: #ffffff; + border: 1px solid #d7dce5; + border-radius: 8px; + padding: 0.85rem 0.95rem; +} + +.zcp-feature-card h3 { + color: #172033; + font-size: 0.92rem; + line-height: 1.25; + margin: 0; +} + +.zcp-feature-card p { + color: #4b5563; + font-size: 0.8rem; + line-height: 1.5; + margin: 0.4rem 0 0; +} + +.zcp-feature-callout { + background: #172033; + border: 1px solid #172033; + border-radius: 8px; + color: #ffffff; + padding: 0.95rem 1.05rem; +} + +.zcp-feature-callout span { + color: #d1d5db; + display: block; + font-size: 0.66rem; + font-weight: 650; + letter-spacing: 0; + line-height: 1.1; + margin-bottom: 0.35rem; + text-transform: uppercase; +} + +.zcp-feature-callout h3 { + color: #ffffff; + font-size: 1rem; + line-height: 1.25; + margin: 0; +} + +.zcp-feature-callout p { + color: #d1d5db; + font-size: 0.84rem; + line-height: 1.5; + margin: 0.45rem 0 0; +} + +@media (min-width: 768px) { + .zcp-features-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.8rem; } + .zcp-feature-callout { grid-column: 1 / -1; padding: 1.05rem 1.2rem; } +} + +html[data-theme='dark'] .zcp-feature-card { background: rgba(24, 24, 27, 0.72); } +html[data-theme='dark'] .zcp-feature-card h3 { color: #f8fafc; } +html[data-theme='dark'] .zcp-feature-card p { color: #cbd5e1; } + +/* AsciiGraph — blended with page, no code-block chrome */ + +.ascii-graph { + background: transparent; + border: none; + color: #475569; + display: block; + font-family: var(--ifm-font-family-monospace); + font-size: 0.88rem; + line-height: 1.4; + margin: 2.5rem auto 3rem; + max-width: 100%; + overflow-x: auto; + padding: 0.5rem 0; + text-align: left; + white-space: pre; + width: fit-content; +} + +.markdown > h2 { + margin-top: 2rem; +} + +.markdown > h3 { + margin-top: 1.75rem; +} + +html[data-theme='dark'] .ascii-graph { + color: #cbd5e1; +} + +.ascii-graph__highlight { + background: rgba(45, 114, 217, 0.14); + border-radius: 2px; + color: #1d4ed8; +} + +html[data-theme='dark'] .ascii-graph__highlight { + background: rgba(96, 165, 250, 0.18); + color: #93c5fd; +} + +.ascii-graph__accent { + opacity: 0.5; +} + +.topology-toggle { + text-align: center; + margin: 1.25rem 0 1.5rem; +} + +.topology-toggle__control { + display: inline-flex; + background: rgba(15, 23, 42, 0.04); + border-radius: 999px; + padding: 3px; + margin-bottom: 0.25rem; +} + +.topology-toggle__button { + appearance: none; + border: 0; + background: transparent; + font-family: var(--ifm-font-family-base); + font-size: 0.85rem; + font-weight: 500; + color: #475569; + padding: 0.35rem 0.95rem; + border-radius: 999px; + cursor: pointer; + transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease; +} + +.topology-toggle__button:hover { + color: #1d4ed8; +} + +.topology-toggle__button--active { + background: #ffffff; + color: #1d4ed8; + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08); +} + +.topology-toggle .ascii-graph { + margin-top: 0.25rem; +} + +.topology-toggle__secondary { + margin-top: 0 !important; + padding-top: 0; +} + +.intro-ascii { + position: relative; + /* Match .ascii-graph's centering convention so this lines up with the + other ASCII diagrams on the page. */ + margin: 2.5rem auto 3rem; + width: fit-content; + max-width: 100%; + overflow-x: auto; + /* Lock font + line-height to .ascii-graph values so that 1ch / 1lh on + absolutely-positioned children align with the pre's character grid. */ + font-family: var(--ifm-font-family-monospace); + font-size: 0.88rem; + line-height: 1.4; +} + +.intro-ascii__frame { + margin: 0; + padding: 0; +} + +.intro-ascii__logo { + position: absolute; + width: 1.7em; + height: 1.7em; + transform: translate(-50%, -50%); + object-fit: contain; + font: inherit; + pointer-events: auto; +} + +html[data-theme='dark'] .topology-toggle__control { + background: rgba(255, 255, 255, 0.06); +} + +html[data-theme='dark'] .topology-toggle__button { + color: #94a3b8; +} + +html[data-theme='dark'] .topology-toggle__button:hover { + color: #93c5fd; +} + +html[data-theme='dark'] .topology-toggle__button--active { + background: rgba(45, 114, 217, 0.22); + color: #93c5fd; + box-shadow: none; +} + +/* page intro hook — the page's positioning statement */ + +.zcp-intro-hook { + background: rgba(45, 114, 217, 0.06); + border-left: 4px solid #2d72d9; + border-radius: 0 8px 8px 0; + color: #172033; + font-size: 1.05rem; + font-weight: 500; + line-height: 1.55; + margin: 1.5rem 0 2.5rem; + padding: 1.1rem 1.35rem; +} + +.zcp-intro-hook p { + margin: 0; +} + +.zcp-intro-hook p + p { + margin-top: 0.7rem; +} + +.zcp-intro-hook strong { + color: #1d4ed8; + font-weight: 650; +} + +html[data-theme='dark'] .zcp-intro-hook { + background: rgba(96, 165, 250, 0.08); + border-left-color: #60a5fa; + color: #f8fafc; +} + +html[data-theme='dark'] .zcp-intro-hook strong { + color: #93c5fd; +} diff --git a/apps/docs/src/plugins/markdown-source/__tests__/converters.test.js b/apps/docs/src/plugins/markdown-source/__tests__/converters.test.js new file mode 100644 index 000000000..2e5e12eba --- /dev/null +++ b/apps/docs/src/plugins/markdown-source/__tests__/converters.test.js @@ -0,0 +1,513 @@ +'use strict'; + +const test = require('node:test'); +const assert = require('node:assert/strict'); +const C = require('../converters'); + +// ---------- Note -------------------------------------------------------- + +test('convertNoteToMarkdown: tip with title -> :::tip[title]', () => { + const input = 'Body text here'; + const out = C.convertNoteToMarkdown(input).trim(); + assert.equal(out, ':::tip[Hello World]\n\nBody text here\n\n:::'); +}); + +test('convertNoteToMarkdown: warning without title -> :::warning', () => { + const input = 'Be careful'; + const out = C.convertNoteToMarkdown(input).trim(); + assert.equal(out, ':::warning\n\nBe careful\n\n:::'); +}); + +test('convertNoteToMarkdown: error type -> :::danger', () => { + const input = 'Something failed'; + const out = C.convertNoteToMarkdown(input).trim(); + assert.match(out, /^:::danger/); +}); + +test('convertNoteToMarkdown: success type -> :::tip', () => { + const input = 'Worked'; + const out = C.convertNoteToMarkdown(input).trim(); + assert.match(out, /^:::tip/); +}); + +test('convertNoteToMarkdown: no type -> :::note', () => { + const input = 'Just a note'; + const out = C.convertNoteToMarkdown(input).trim(); + assert.equal(out, ':::note[Heads up]\n\nJust a note\n\n:::'); +}); + +test('convertNoteToMarkdown: multi-line body with markdown preserved', () => { + const input = ` +Line one +**bold** here + +Another paragraph with \`code\`. +`; + const out = C.convertNoteToMarkdown(input); + assert.match(out, /:::tip\[Multi\]/); + assert.match(out, /Line one/); + assert.match(out, /\*\*bold\*\* here/); + assert.match(out, /Another paragraph with `code`\./); + assert.match(out, /:::\n$/); +}); + +test('convertNoteToMarkdown: multiple Notes in one document', () => { + const input = `first +some text +second`; + const out = C.convertNoteToMarkdown(input); + assert.match(out, /:::tip\[A\]/); + assert.match(out, /:::warning/); + assert.match(out, /some text/); +}); + +// ---------- AsciiGraph ------------------------------------------------- + +test('convertAsciiGraphToMarkdown: unwraps JSX template literals into code fence', () => { + const input = '{`hello world`}'; + const out = C.convertAsciiGraphToMarkdown(input).trim(); + assert.equal(out, '```text\nhello world\n```'); +}); + +test('convertAsciiGraphToMarkdown: strips inline span highlights', () => { + const input = '{`foo `}{`HL`}{` bar`}'; + const out = C.convertAsciiGraphToMarkdown(input).trim(); + assert.equal(out, '```text\nfoo HL bar\n```'); +}); + +test('convertAsciiGraphToMarkdown: preserves multi-line ASCII art', () => { + const input = `{\` \`}{\`zcp\`}{\` + │ + ▼ + edit code\`}`; + const out = C.convertAsciiGraphToMarkdown(input); + assert.match(out, /```text/); + assert.match(out, / zcp/); + assert.match(out, / │/); + assert.match(out, / ▼/); + assert.match(out, / edit code/); + assert.doesNotMatch(out, / { + const input = '{`x`}'; + const out = C.convertAsciiGraphToMarkdown(input); + assert.doesNotMatch(out, /ariaLabel/); +}); + +test('convertAsciiGraphToMarkdown: handles two AsciiGraphs in same document', () => { + const input = '{`one`}\nbetween\n{`two`}'; + const out = C.convertAsciiGraphToMarkdown(input); + assert.match(out, /one/); + assert.match(out, /between/); + assert.match(out, /two/); + assert.equal((out.match(/```text/g) || []).length, 2); +}); + +// ---------- DocCardList ------------------------------------------------ + +test('convertDocCardListToMarkdown: single self-closing item with description', () => { + const input = ``; + const out = C.convertDocCardListToMarkdown(input); + assert.match(out, /- \[Quickstart\]\(\/zcp\/quickstart\) — AI Agent recipe and a real change in five minutes\./); +}); + +test('convertDocCardListToMarkdown: multiple items with Icons[...] inside', () => { + const input = ``; + const out = C.convertDocCardListToMarkdown(input); + assert.match(out, /- \[A\]\(\/a\) — desc A/); + assert.match(out, /- \[B\]\(\/b\) — desc B/); + assert.match(out, /- \[C\]\(\/c\) — desc C/); +}); + +test('convertDocCardListToMarkdown: item without description renders link only', () => { + const input = ``; + const out = C.convertDocCardListToMarkdown(input); + assert.match(out, /- \[X\]\(\/x\)\n/); + assert.doesNotMatch(out, /—/); +}); + +test('convertDocCardListToMarkdown: surrounding text is preserved', () => { + const input = `before + +after`; + const out = C.convertDocCardListToMarkdown(input); + assert.match(out, /before/); + assert.match(out, /after/); + assert.match(out, /- \[X\]\(\/x\)/); +}); + +test('convertDocCardListToMarkdown: items with single-quoted strings containing ]', () => { + // Critical regression test: `customProps: { icon: Icons['x'] }` contains `]`, + // which would terminate a naive lazy regex match. + const input = ``; + const out = C.convertDocCardListToMarkdown(input); + assert.match(out, /- \[P\]\(\/p\) — d/); +}); + +// ---------- Badge ------------------------------------------------------ + +test('convertBadgeToMarkdown: optional -> *[Optional]*', () => { + assert.equal(C.convertBadgeToMarkdown(''), '*[Optional]*'); +}); + +test('convertBadgeToMarkdown: required -> *[Required]*', () => { + assert.equal(C.convertBadgeToMarkdown(''), '*[Required]*'); +}); + +test('convertBadgeToMarkdown: required-some -> longer label', () => { + assert.equal( + C.convertBadgeToMarkdown(''), + '*[Required for some runtimes]*' + ); +}); + +test('convertBadgeToMarkdown: custom with text', () => { + assert.equal( + C.convertBadgeToMarkdown(''), + '*[Beta]*' + ); +}); + +test('convertBadgeToMarkdown: no attributes defaults to optional', () => { + assert.equal(C.convertBadgeToMarkdown(''), '*[Optional]*'); +}); + +// ---------- Button ----------------------------------------------------- + +test('convertButtonToMarkdown: unwraps children', () => { + const input = ''; + assert.equal(C.convertButtonToMarkdown(input), 'click'); +}); + +// ---------- Link ------------------------------------------------------- + +test('convertLinkToMarkdown: plain link', () => { + assert.equal( + C.convertLinkToMarkdown('Read more'), + '[Read more](/features/infrastructure)' + ); +}); + +test('convertLinkToMarkdown: link with inline code', () => { + assert.equal( + C.convertLinkToMarkdown('use zcli'), + '[use `zcli`](/x)' + ); +}); + +test('convertLinkToMarkdown: no href returns children unchanged', () => { + assert.equal( + C.convertLinkToMarkdown('orphan'), + 'orphan' + ); +}); + +// ---------- CustomCard ------------------------------------------------- + +test('convertCustomCardToMarkdown: title + emoji -> ### emoji title', () => { + const input = 'body content'; + const out = C.convertCustomCardToMarkdown(input).trim(); + assert.equal(out, '### 🚀 No Fuss\n\nbody content'); +}); + +test('convertCustomCardToMarkdown: title only', () => { + const input = 'stuff'; + const out = C.convertCustomCardToMarkdown(input).trim(); + assert.equal(out, '### Just Title\n\nstuff'); +}); + +// ---------- DeployButton ----------------------------------------------- + +test('convertDeployButtonToMarkdown: builds recipe URL', () => { + assert.equal( + C.convertDeployButtonToMarkdown(''), + '[Deploy "deno" recipe on Zerops](https://app.zerops.io/recipe/?lf=deno)' + ); +}); + +test('convertDeployButtonToMarkdown: missing link -> empty', () => { + assert.equal( + C.convertDeployButtonToMarkdown(''), + '' + ); +}); + +// ---------- Dropdown --------------------------------------------------- + +test('convertDropdownToMarkdown: single item -> #### heading + body', () => { + const input = 'body here'; + const out = C.convertDropdownToMarkdown(input); + assert.match(out, /#### X/); + assert.match(out, /body here/); +}); + +test('convertDropdownToMarkdown: multiple items', () => { + const input = ` +first body +second body +`; + const out = C.convertDropdownToMarkdown(input); + assert.match(out, /#### One/); + assert.match(out, /first body/); + assert.match(out, /#### Two/); + assert.match(out, /second body/); +}); + +// ---------- Video ------------------------------------------------------ + +test('convertVideoToMarkdown: self-closing with src', () => { + assert.equal( + C.convertVideoToMarkdown('
@@ -160,7 +160,7 @@ With the model clear, here's where ZCP sits among adjacent tools. People say "ag

Replit Agent · Lovable · Bolt.new · v0 · Devin · OpenHands

-

The agent, the workspace, the stack, and (often) the hosting target are fused into one closed product. Fast for prototypes. Decisions are made for you. Switching agents usually means switching platforms, and production-grade primitives — multi-environment promotion, private networking, real observability — tend to be limited or absent. **ZCP doesn't bundle**: the agent and model stay yours on your own subscription, and the platform underneath is normal Zerops with the full stack of managed services, networking, deploys, and observability.

+

The agent, the workspace, the stack, and (often) the hosting target are fused into one closed product. Fast for prototypes. Decisions are made for you. Switching agents usually means switching platforms, and production-grade primitives — a real dev/stage/prod release flow, private networking, observability — tend to be limited or absent. **ZCP doesn't bundle**: the agent and model stay yours on your own subscription, and the platform underneath is normal Zerops with the full stack of managed services, networking, deploys, and observability.

@@ -213,7 +213,7 @@ With the model clear, here's where ZCP sits among adjacent tools. People say "ag diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx index 379887f97..c1124ff8d 100644 --- a/apps/docs/content/zcp/concept/how-it-works.mdx +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -1,15 +1,15 @@ --- title: 'How it works' -description: 'The operating loop for live state, runtime target, service setup, app work, verification, recovery, and delivery.' +description: 'The work loop: live state, runtime target, service setup, app work, verification, recovery, and delivery.' --- -The ZCP MCP setup gives a coding agent an operating loop for Zerops app work. The agent reads live state, chooses the app runtime and dependencies, changes the app, deploys through Zerops, verifies real behavior, and returns proof or a blocker. +The ZCP MCP setup gives a coding agent a **work loop** for Zerops app work. The agent reads live state, chooses the app runtime and dependencies, changes the app, deploys through Zerops, verifies real behavior, and returns proof or a blocker. -The loop is carried by bounded Zerops operations, Zerops-specific knowledge, and instructions for what to inspect, what to change, when to ask, and what counts as done. You describe the product outcome; the agent uses the loop to make its decisions visible while it works. +The loop is carried by project-scoped Zerops operations, Zerops-specific knowledge, and instructions for what to inspect, what to change, when to ask, and what counts as done. You describe the product outcome; the agent uses the loop to make its decisions visible while it works. The source of truth is the real project: runtimes, managed services, env references, logs, events, and deploy results. A separate preview sandbox is not the source of truth. -## The control loop +## The work loop ```mermaid flowchart TD @@ -73,19 +73,19 @@ Chat history is not the source of truth. If the agent sounds confused, starts fr Read current project status and tell me where this project stands before changing anything. ``` -## What the workflow coordinates +## What the workflow handles -The generated workflow is opinionated about the concerns an agent must resolve during app work. You usually do not name them in the prompt. The tools and instructions supply the state, guidance, operations, and done criteria so the agent can work from evidence. +The workflow handles the things an agent must resolve during app work so you do not have to name them in the prompt. The tools and instructions supply the state, guidance, operations, and verification surface so the agent can work from evidence. -| Concern | What the agent gets | What that means for you | -| ---------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| What the workflow handles | What the agent gets | What that means for you | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | Live state | Services, env-var keys, Zerops references, recent events, logs, verification output, and saved work state. | You do not have to paste a service inventory or reconstruct what happened. | | Runtime target | Rules for choosing the app runtime that should receive code changes. | The agent can identify where app work belongs before editing or deploying. | | Managed services | Knowledge and env wiring patterns for database, cache, queue, search, storage, mail, and similar services. | Product intent can imply real dependencies without a manual wiring checklist. | | Service setup | Operations for using existing services or creating missing runtimes and dependencies. | Runtime layout and dependencies can be established before app work starts. | | App wiring | Zerops-specific rules for `zerops.yaml`, env references, ports, commands, public access, and build/deploy behavior. | The app is wired for Zerops rather than for a generic cloud template. | | Deploy evidence | Build logs, runtime logs, platform events, service status, HTTP checks, and structured verification output. | A failed deploy becomes a diagnosis surface instead of guesswork. | -| Behavior proof | A done gate based on the requested behavior, not only on a successful build or reachable root URL. | The final answer can point to what was actually checked. | +| Behavior proof | The proof gate is the requested behavior, not only a successful build or reachable root URL. | The final answer can point to what was actually checked. | | Delivery handoff | Direct proof first; then git push, CI, or human handoff when that is the chosen delivery choice. | Shipping setup follows a verified running result. | The exact labels for layouts, delivery modes, and setup routes live in the [Glossary](/zcp/glossary) and [Workflows in depth](/zcp/reference/agent-workflow). @@ -119,16 +119,7 @@ The first functional deploy goes directly through MCP so the agent has a running ## Recovery is evidence-driven -When something fails, the agent has the evidence surface that matches the failure: - -| Failure pattern | Useful evidence | -| ------------------------ | ------------------------------------------------------------------------------------------------------------ | -| Build failed | Build logs, build commands, dependency manifests, deploy file list. | -| Runtime failed to start | Runtime or prepare logs, start command, ports, env references. | -| Route or behavior failed | Verify output, HTTP response, runtime logs at request time, stored state. | -| Network access failed | VPN, SSH, DNS, subdomain readiness, service status, named transport error. | -| Config was rejected | Field-level rejection, `zerops.yaml`, setup name, env reference, service settings. | -| Credential failed | The named credential surface: Zerops token, git token, SSH, managed-service credential, or external API key. | +When something fails, the workflow puts the agent on the matching evidence surface — build logs for build failures, prepare/runtime logs for start failures, verify output and request-time logs for behavior failures, transport surfaces for network failures, field-level rejection for config, and the named credential surface for credential failures. The full categories, what to read first, and what to avoid live in [Troubleshooting](/zcp/reference/troubleshooting). Retrying the same deploy without new evidence is not progress. The loop pushes toward one of three outcomes: fix from a cause, ask for the missing decision, or report a blocker with the category, evidence read, and attempts made. @@ -149,13 +140,7 @@ The final answer should make proof inspectable: runtime name, URL or endpoint, b ## Delivery happens after proof -Delivery choice controls how future changes ship after a verified runtime exists: - -| Choice | Use when | -| ------------------ | ------------------------------------------------------------------------------ | -| Keep direct deploy | Early development, demos, dev/stage iteration, or agent-owned runtime changes. | -| Push to git | You want commits, review, repository history, or a repo-triggered build. | -| External handoff | CI, release management, or a human owns the next deploy. | +Delivery choice controls how future changes ship after a verified runtime exists: keep direct deploy, push to git, or hand off to CI/release/human action. The user-facing version of this choice and what to tell the agent for each option lives in [Build and ship → Choose delivery after proof](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). Packaging a running service turns a deployed runtime into a re-importable bundle for another Zerops project. It is useful for handoff or reuse after proof, not for deploying the next app change; see [Package a running service](/zcp/workflows/package-running-service). @@ -186,5 +171,5 @@ That is the practical difference between "the agent wrote code" and "the app tas ## Next steps -- [Build and ship](/zcp/workflows/build-with-zcp) - normal app work after setup. -- [Workflows in depth](/zcp/reference/agent-workflow) - process gates, generated files, runtime layouts, delivery terms, and completion evidence. +- [Build and ship](/zcp/workflows/build-with-zcp) — normal app work after setup. +- [Workflows in depth](/zcp/reference/agent-workflow) — process gates, generated files, runtime layouts, delivery labels, and completion evidence. diff --git a/apps/docs/content/zcp/glossary.mdx b/apps/docs/content/zcp/glossary.mdx index d3ab11b31..b9012bcdc 100644 --- a/apps/docs/content/zcp/glossary.mdx +++ b/apps/docs/content/zcp/glossary.mdx @@ -11,7 +11,7 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee **MCP server** - the Model Context Protocol server exposed by the `zcp` binary. -**ZCP MCP tools** - the bounded Zerops operations exposed to an agent or MCP-capable client. In MCP clients, this usually appears as the `zerops` server. +**ZCP MCP tools** - the project-scoped Zerops operations exposed to an agent or MCP-capable client. In MCP clients, this usually appears as the `zerops` server. **`zcp` binary** - the executable that can run inside remote setup or on your machine in local setup. @@ -61,7 +61,7 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee **Runtime target** - the app runtime selected for the current change, such as `appdev`, `appstage`, `app`, or a linked local target. -**Runtime layout** - which app runtime services the workflow should use: +**Runtime layout** - which app runtime services the workflow should use: - `standard` - dev runtime plus explicit stage runtime. - `dev` - one mutable development runtime. @@ -97,7 +97,7 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee **Package a running service** - workflow that turns one verified runtime and its managed dependencies into a re-importable, git-backed Zerops bundle. -**Production promotion** - handoff from verified dev or stage work into a separate production Zerops project through production infrastructure setup, production deploy trigger setup, and repeatable releases. +**Production release** - the release operation that moves verified dev or stage work into a separate production Zerops project. It is set up once per project (production infrastructure) and once per runtime (production deploy trigger), then runs every release. **Production boundary** - policy that production should live in a separate Zerops project without a `zcp` service and receive promoted work through CI, release process, or human action. @@ -107,7 +107,7 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee **Public subdomain access** - Zerops `.zerops.app` URL access for an eligible HTTP runtime. Workers and non-HTTP services do not get useful public URLs. -**Failure category** - label that points to the first useful evidence surface: +**Failure category** - label that points to the first useful evidence surface: - `build` - build phase failed. - `start` - build passed, but runtime start or prepare failed. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index 1d2893276..ddaa5138f 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -5,23 +5,28 @@ description: 'How the ZCP MCP server lets a coding agent work with one real Zero import Image from '/src/components/Image'; -Zerops provides the project infrastructure: runtimes, managed services, networking, env vars, deploys, logs, and public access. ZCP MCP is the project-scoped tool layer that lets a coding agent work with that infrastructure through MCP. +:::info Public preview +ZCP MCP is a public preview and is under active development. If you hit a bug, confusing behavior, or a missing workflow, please report it on Discord; feedback from real projects is especially useful. +::: -For the broader feature concept — why coding agents need project infrastructure rather than a sandbox or generated artifact — start with [Infrastructure for Coding Agents](/features/coding-agents). +This section covers, in order: -With the generated workflow instructions enabled, an app task should end in proof or a blocker. Proof means a deployed runtime plus the URL, endpoint response, UI state, worker result, or stored data that shows the requested behavior works. A blocker means the agent read the relevant Zerops evidence and names the missing credential, decision, unsupported fit, or repeated failure. +- **How a run unfolds.** [Quickstart](/zcp/quickstart) for hands-on; [How it works](/zcp/concept/how-it-works) for the work loop. +- **Where the agent runs.** [Remote or local setup](/zcp/setup/choose-workspace), [Trust model](/zcp/security/trust-model), and [Production boundary](/zcp/security/production-policy). +- **What you decide while working.** [Build and ship](/zcp/workflows/build-with-zcp), [Package a running service](/zcp/workflows/package-running-service), [Promote to production](/zcp/workflows/promote-to-production). +- **Exact labels when they matter.** [Workflows in depth](/zcp/reference/agent-workflow), [ZCP MCP tools](/zcp/reference/mcp-operations), [Troubleshooting](/zcp/reference/troubleshooting), [Glossary](/zcp/glossary). -Your prompt can stay about the product. Name the stack, runtime layout, acceptance criteria, delivery path, external credentials, or risky approval only when those decisions matter. +For the broader feature concept — why coding agents need real project infrastructure rather than a sandbox or generated artifact — start with [Infrastructure for Coding Agents](/features/coding-agents). -:::info Public preview -ZCP MCP is a public preview and is currently under active development. If you hit a bug, confusing behavior, or a missing workflow, please report it on Discord; feedback from real projects is especially useful. -::: +With the generated workflow instructions enabled, an app task ends in proof or a blocker. **Proof** is a deployed runtime plus the URL, endpoint response, UI state, worker result, or stored data that shows the requested behavior works. A **blocker** is the agent reading the relevant Zerops evidence and naming the missing credential, decision, unsupported fit, or repeated failure. + +Your prompt can stay about the product. Name the stack, runtime layout, acceptance criteria, delivery path, external credentials, or risky approval only when those decisions matter. ## What the agent gets **Current state.** Services, runtime layout, managed dependencies, env-var keys and references, logs, events, deploy history, verification state, and saved work state. -**Zerops operations.** Tools for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. +**Zerops operations.** Project-scoped tools for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. **Workflow.** The generated instructions combine service setup and app development: inspect state, choose the runtime target, use or create services, wire code and `zerops.yaml`, deploy, verify, and choose delivery. The Zerops work stays behind the product task instead of becoming another checklist. @@ -64,32 +69,9 @@ To start, add remote setup in Zerops or initialize local setup beside your edito **Workspace freedom.** The `zcp@1` service is still a normal Zerops service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and adapt the workspace. Details live in [What remote workspace gives you](/zcp/setup/hosted-workspace#make-customization-persistent). :::caution Production boundary -Use this setup for development or staging work. Production should stay in a separate Zerops project and receive promoted work through your CI or release process; see [Promote to production](/zcp/workflows/promote-to-production) for the practical flow and [Production boundary](/zcp/security/production-policy) for the policy. +Use this setup for development or staging work. Production should stay in a separate Zerops project and receive released work through your CI or release process; see [Promote to production](/zcp/workflows/promote-to-production) for the practical flow and [Production boundary](/zcp/security/production-policy) for the policy. ::: -## Start here - -First-read pages: - -| If you want to | Read | -| ----------------------------------------- | -------------------------------------------------------- | -| Try the guided hands-on path | [Quickstart](/zcp/quickstart) | -| Understand the agent operating loop | [How it works](/zcp/concept/how-it-works) | -| Compare remote and local setup | [Remote or local setup](/zcp/setup/choose-workspace) | -| Start building after setup | [Build and ship](/zcp/workflows/build-with-zcp) | - -Specific tasks and reference: - -| If you want to | Read | -| ---------------------------------------- | --------------------------------------------------------------------------- | -| Understand remote workspace | [What remote workspace gives you](/zcp/setup/hosted-workspace) | -| Use your local editor or CLI agent | [Run locally](/zcp/setup/local-agent-bridge) | -| Decide how finished work ships | [Build and ship](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | -| Understand token access and boundaries | [Trust model](/zcp/security/trust-model) | -| Understand the feature concept | [Infrastructure for Coding Agents](/features/coding-agents) | -| Understand workflow-guided runs | [Workflows in depth](/zcp/reference/agent-workflow) | -| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | - ## What stays outside This section is not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), networking, scaling, and service references remain canonical for platform behavior. diff --git a/apps/docs/content/zcp/quickstart.mdx b/apps/docs/content/zcp/quickstart.mdx index 76b50422d..c41570832 100644 --- a/apps/docs/content/zcp/quickstart.mdx +++ b/apps/docs/content/zcp/quickstart.mdx @@ -107,20 +107,20 @@ If the agent cannot finish, useful output names the blocker: missing credential, diff --git a/apps/docs/content/zcp/reference/agent-workflow.mdx b/apps/docs/content/zcp/reference/agent-workflow.mdx index 8517af9e8..e2ed76ebe 100644 --- a/apps/docs/content/zcp/reference/agent-workflow.mdx +++ b/apps/docs/content/zcp/reference/agent-workflow.mdx @@ -1,9 +1,9 @@ --- title: 'Workflows in depth' -description: 'The bootstrap and develop process behind workflow-guided ZCP MCP agent runs.' +description: 'The bootstrap and develop process behind generated-workflow ZCP MCP agent runs.' --- -Use this page when you want to understand the process behind workflow-guided agent work. Normal prompts should still describe outcomes. The workflow exists so the agent can read the project, prepare the right runtime and services, make the app change, deploy, verify, recover from evidence, and stop with proof or a concrete blocker. +Use this page when you want the process and exact labels behind a run that follows the generated workflow. Normal prompts should still describe outcomes. The workflow exists so the agent can read the project, prepare the right runtime and services, make the app change, deploy, verify, recover from evidence, and stop with proof or a concrete blocker. The useful mental split is: @@ -12,7 +12,7 @@ The useful mental split is: | **Bootstrap** | Where the app should run and which services it depends on. | Runtime target and managed dependencies are known. | | **Develop** | Code, `zerops.yaml`, env wiring, deploy, verification, recovery, and delivery choice. | The requested behavior is proved, or the blocker is concrete. | -Bootstrap is not a marketing or onboarding step. It is the workflow's name for "make the project layout safe to work in before changing app code." +For the loop itself, see [How it works](/zcp/concept/how-it-works). This page catalogs the exact phases, routes, layouts, labels, and gates. ## Session layers @@ -28,7 +28,7 @@ The `zcp` service is the control surface, not the app runtime. ## What drives the workflow -The workflow-guided experience is made from four pieces: +The generated workflow is made from four pieces: | Piece | Role | | ----- | ---- | @@ -75,42 +75,13 @@ Bootstrap ends when the app runtime target and managed dependencies are known. I ## Runtime layouts -Runtime layout describes which app runtime services the workflow should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. +Runtime layout describes which app runtime services the workflow should use. Exact layout labels (`standard`, `dev`, `simple`, `local-stage`, `local-only`) and the runtime names they map to live in the [Glossary](/zcp/glossary#runtime-layout). The user-facing three (dev, dev + stage, stage / linked target) are in [Build and ship → Choose the runtime layout](/zcp/workflows/build-with-zcp#choose-the-runtime-layout). Service scaling mode (`HA`/`NON_HA`) is a separate service setting. -| Layout | Meaning | Typical names | -| ------ | ------- | ------------- | -| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage is explicit. | `appdev` + `appstage` | -| `dev` | One mutable development runtime. | `appdev` | -| `simple` | One runtime with no dev/stage split. | `app` | -| `local-stage` | Local source directory linked to one Zerops runtime for deploys. | local directory + `appstage` | -| `local-only` | Local source directory with no linked deploy target yet. | local directory | - -In `standard`, stage is explicit. Work scoped to `appdev` does not silently touch `appstage`; promotion or stage verification happens when the user asks for it. +In `standard`, stage is explicit. Work scoped to `appdev` does not silently touch `appstage`; a release or stage verification happens when the user asks for it. ## Develop -Develop is the main app-work loop. It begins after bootstrap has a runtime target and dependencies. It closes only when runtime reachability and requested behavior both pass, or when the agent has a blocker that needs a human decision. - -```mermaid -flowchart TD - scope["1. Name runtime target"] - change["2. Change code/config"] - deploy["3. Direct deploy"] - serve["4. Start or restart if needed"] - reach{"5. Runtime reachable?"} - behavior{"6. Requested behavior works?"} - done(["Proof"]) - fix["7. Categorize failure
read evidence, fix"] - blocker(["Blocker"]) - - scope --> change --> deploy --> serve --> reach - reach -- yes --> behavior - reach -- no --> fix - behavior -- yes --> done - behavior -- no --> fix - fix --> deploy - fix --> blocker -``` +Develop is the main app-work loop. It begins after bootstrap has a runtime target and dependencies. It closes only when runtime reachability and requested behavior both pass, or when the agent has a blocker that needs a human decision. The loop itself is in [How it works → The work loop](/zcp/concept/how-it-works#the-work-loop). The labeled steps are: 1. **Name runtime target.** State which runtime is in scope: `appdev`, `appstage`, `app`, or a linked local target. 2. **Change code and config.** Edit app files, `zerops.yaml`, env references, migrations, seeds, framework config, or local `.env` bridge when needed. @@ -124,19 +95,9 @@ Reachability and requested behavior are separate gates. A green deploy with a br ## Failure categories -Failure categories point the agent to the first useful evidence surface. - -| Category | What it means | Read first | -| -------- | ------------- | ---------- | -| `build` | Build phase failed. | Build logs, build commands, dependency manifests, deploy file list. | -| `start` | Build passed, but runtime start or prepare failed. | Prepare/runtime logs, start command, ports, env references. | -| `verify` | Runtime exists, but reachability or behavior failed. | Failing check detail, HTTP response, request-time runtime logs, behavior evidence. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | VPN, SSH, DNS, subdomain readiness, service status, named transport error. | -| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Field-level rejection, setup name, env reference, service settings. | -| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | -| `other` | No known category matched. | Raw event/log evidence; stop if repeated without a new signal. | +Failure categories (`build`, `start`, `verify`, `network`, `config`, `credential`, `other`) point the agent to the first useful evidence surface. The full read-first / avoid playbook is in [Troubleshooting → Evidence order](/zcp/reference/troubleshooting#evidence-order); the term definitions are in the [Glossary](/zcp/glossary#failure-category). -Categorization is what turns retries into evidence-driven fixes. The practical recovery flow lives in [Troubleshooting](/zcp/reference/troubleshooting). +Categorization is what turns retries into evidence-driven fixes. ## Delivery after proof @@ -150,7 +111,7 @@ Delivery mode applies after a verified deploy. It does not replace the first pro Git-push capability, delivery mode, and build integration are separate. A project can have git-push configured while still using direct deploy for a given session. -Packaging and production promotion are deliberate handoffs after proof. Packaging turns a verified runtime into a git-backed import bundle. Production promotion moves verified work into a separate production project through GUI setup, git/CI triggers, or the team's release process. +Packaging and production release are deliberate handoffs after proof. Packaging turns a verified runtime into a git-backed import bundle. A production release moves verified work into a separate production project through GUI setup, git/CI triggers, or the team's release process. ## Generated files and state @@ -190,16 +151,11 @@ A clear blocker is acceptable completion only when it names the runtime in scope ## Confirmation gates -Some operations pause because the loss is not safely reversible from inside the conversation: - -- **Service deletion** requires explicit user approval in the current conversation, by service name. -- **Destructive import override** first refuses and names what would be replaced; a second call must acknowledge the same targets. - -See [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) for the user-facing confirmation flow. +Two operations pause because the loss is not safely reversible from inside the conversation: **service deletion** (named approval) and **destructive import override** (refuse-then-acknowledge). See [Tokens and credentials → What ZCP enforces for destructive actions](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) for the user-facing confirmation flow. ## Auditing a workflow -A workflow-guided run is well-shaped if the evidence answers: +A workflow run is well-shaped if the evidence answers: | Question | Evidence | | -------- | -------- | @@ -208,10 +164,10 @@ A workflow-guided run is well-shaped if the evidence answers: | Which runtime was developed and deployed? | Runtime target, deploy result, events, and logs. | | What proved reachability? | Verify output, service status, public URL, or HTTP probe. | | What proved behavior? | Endpoint, UI flow, job result, database/object state, or other requested proof. | -| What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production handoff note. | +| What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production release handoff note. | ## Related reference -- [ZCP MCP tools](/zcp/reference/mcp-operations) - operation names and direct tool calls. -- [Troubleshooting](/zcp/reference/troubleshooting) - recovery order when a run gets stuck. -- [Glossary](/zcp/glossary) - exact terms used across the ZCP MCP reference. +- [ZCP MCP tools](/zcp/reference/mcp-operations) — operation names and direct tool calls. +- [Troubleshooting](/zcp/reference/troubleshooting) — recovery order when a run gets stuck. +- [Glossary](/zcp/glossary) — exact terms used across the ZCP MCP reference. diff --git a/apps/docs/content/zcp/reference/index.mdx b/apps/docs/content/zcp/reference/index.mdx index aa43a11b7..c83795b4c 100644 --- a/apps/docs/content/zcp/reference/index.mdx +++ b/apps/docs/content/zcp/reference/index.mdx @@ -3,11 +3,19 @@ title: "Reference" description: "Process details, MCP tool names, recovery guidance, and glossary for ZCP MCP." --- -Use reference pages when exact labels matter. Day-to-day app work starts in [Build and ship](/zcp/workflows/build-with-zcp). - -| Need | Page | -|---|---| -| Understand what sits behind workflow-guided agent runs: generated instructions, state, process gates, and completion evidence | [Workflows in depth](/zcp/reference/agent-workflow) | -| Use ZCP MCP tools directly or look up operation names for a custom MCP integration | [ZCP MCP tools](/zcp/reference/mcp-operations) | -| Recover a stuck or confused session | [Troubleshooting](/zcp/reference/troubleshooting) | -| Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | +Reference pages exist for the moments when an exact label, tool name, or recovery step matters. For day-to-day app work, start in [Build and ship](/zcp/workflows/build-with-zcp); for the loop itself, [How it works](/zcp/concept/how-it-works). + +## When to reach for which page + +| Reading because… | Read | Sibling page you usually want next | +|---|---|---| +| You want the catalog of phases, routes, layouts, and labels behind a generated-workflow run | [Workflows in depth](/zcp/reference/agent-workflow) | [Glossary](/zcp/glossary) for definitions, [Troubleshooting](/zcp/reference/troubleshooting) for stuck runs | +| You are using ZCP MCP from a custom client, or you need exact tool names for agent policy | [ZCP MCP tools](/zcp/reference/mcp-operations) | [Tokens and credentials](/zcp/security/tokens-and-project-access) for what a token allows | +| The agent is stuck, a session was interrupted, deploys keep failing, or you are taking over manually | [Troubleshooting](/zcp/reference/troubleshooting) | [Workflows in depth](/zcp/reference/agent-workflow) for the labels evidence will surface | +| A page, workflow status, or handoff used a term you want to pin down (`appdev`, `local-stage`, `delivery mode`, etc.) | [Glossary](/zcp/glossary) | The page that referenced the term | + +## Common combinations + +- **Debugging a stuck deploy.** [Troubleshooting → Evidence order](/zcp/reference/troubleshooting#evidence-order) names what to read first; [Workflows in depth → Failure categories](/zcp/reference/agent-workflow#failure-categories) maps the labels. +- **Building a custom MCP client.** [ZCP MCP tools](/zcp/reference/mcp-operations) for the tool surface; [Workflows in depth → What drives the workflow](/zcp/reference/agent-workflow#what-drives-the-workflow) for what the generated workflow adds on top of bare tools. +- **Auditing a finished run.** [Workflows in depth → Completion evidence](/zcp/reference/agent-workflow#completion-evidence) for what should be in the final answer; [Trust model → Audit evidence](/zcp/security/trust-model#audit-evidence) for what Zerops records platform-side. diff --git a/apps/docs/content/zcp/reference/mcp-operations.mdx b/apps/docs/content/zcp/reference/mcp-operations.mdx index 6ed2443a4..582b61ad5 100644 --- a/apps/docs/content/zcp/reference/mcp-operations.mdx +++ b/apps/docs/content/zcp/reference/mcp-operations.mdx @@ -11,7 +11,7 @@ ZCP MCP is the MCP server exposed by the `zcp` binary. Any MCP-capable client ca The MCP server exposes operations. It does not, by itself, decide the whole app lifecycle. -Workflow-guided Claude Code uses generated instructions to decide sequencing: inspect state before changing things, choose the runtime target, use existing services or create missing ones, deploy, read failure evidence, verify requested behavior, and stop with proof or a blocker. +Claude Code with the generated workflow uses its instructions to decide sequencing: inspect state before changing things, choose the runtime target, use existing services or create missing ones, deploy, read failure evidence, verify requested behavior, and stop with proof or a blocker. Tool-only use is valid when you want a custom MCP client, script, dashboard, policy-gated agent, or narrow operational task. In that setup, your integration owns the sequencing: @@ -98,6 +98,6 @@ See [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-en ## Related reference -- [Workflows in depth](/zcp/reference/agent-workflow) - how workflow-guided runs use these tools. +- [Workflows in depth](/zcp/reference/agent-workflow) - how a generated-workflow run uses these tools. - [Troubleshooting](/zcp/reference/troubleshooting) - evidence order when a run gets stuck. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, and confirmation gates. diff --git a/apps/docs/content/zcp/security/production-policy.mdx b/apps/docs/content/zcp/security/production-policy.mdx index 89fdf5fbb..202716c20 100644 --- a/apps/docs/content/zcp/security/production-policy.mdx +++ b/apps/docs/content/zcp/security/production-policy.mdx @@ -7,7 +7,7 @@ Use the MCP setup in development or staging. Keep production in a separate Zerop Zerops does not prevent you from adding `zcp` to production. The policy exists because ZCP MCP gives a coding agent operational access. In production, that is the wrong blast radius for normal app development. -The production boundary does not make ZCP output disposable. The intended path is production-shaped dev or stage infrastructure, verified behavior, and then a controlled production promotion outside the agent loop. +The production boundary does not make ZCP output disposable. The intended path is production-shaped dev or stage infrastructure, verified behavior, and then a controlled production release outside the agent loop. ## Recommended project layout @@ -29,7 +29,7 @@ This separation matters even when the same source repository deploys to both pro ## What stage proves -Stage is the production-like rehearsal inside development or staging. It is where the agent proves the change before promotion leaves the ZCP loop. +Stage is the production-like rehearsal inside development or staging. It is where the agent proves the change before the release leaves the ZCP loop. Use stage to match production where it matters: @@ -41,9 +41,9 @@ Use stage to match production where it matters: Stage is not production. It should use non-production data and non-production secrets. A green stage means the change is ready for release, not that the agent should deploy to production itself. -## Promotion +## The release -After stage verifies, ZCP's job is done for that change. Promotion happens outside the agent loop: +After stage verifies, ZCP's job is done for that change. The production release happens outside the agent loop: - CI deploys a merged commit or release tag to the production project. - A release pipeline runs `zcli push` with a production-scoped `ZEROPS_TOKEN`. @@ -76,7 +76,7 @@ If a production deploy fails, investigate in the production project with product ## Acceptable agent involvement -The agent can still help before production promotion: +The agent can still help before the production release: - make the code change in development, - deploy and verify dev/stage runtimes, @@ -88,6 +88,6 @@ ZCP's involvement stops at the handoff. Production execution belongs to the rele ## Related security -- [Promote to production](/zcp/workflows/promote-to-production) - practical production promotion paths after ZCP proof. +- [Promote to production](/zcp/workflows/promote-to-production) - practical production release paths after ZCP proof. - [Trust model](/zcp/security/trust-model) - the boundary that makes this policy enforceable. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - production and development credentials stay separate. diff --git a/apps/docs/content/zcp/security/trust-model.mdx b/apps/docs/content/zcp/security/trust-model.mdx index 5327124da..f7fc945f8 100644 --- a/apps/docs/content/zcp/security/trust-model.mdx +++ b/apps/docs/content/zcp/security/trust-model.mdx @@ -44,9 +44,7 @@ There are three separate credential surfaces people often mix together: | Agent subscription or model API key | You / the agent provider | Lets the coding agent run. Zerops does not provide it. | | Git or CI credentials | You / your repository system | Lets finished work push to git or deploy through CI. | -Remote setup injects `ZCP_API_KEY` into the `zcp` service. Local setup reads it from the `.mcp.json` env block. In both paths, the agent account is still authenticated through the agent's own login flow. - -Details: [Tokens and credentials](/zcp/security/tokens-and-project-access). +In both setups, the agent account is still authenticated through the agent's own login flow. For where each credential lives (remote injection vs `.mcp.json`), rotation, rejected token shapes, and `GIT_TOKEN` / `ZEROPS_TOKEN`, see [Tokens and credentials](/zcp/security/tokens-and-project-access). ## What the token lets the agent do @@ -81,14 +79,7 @@ This is why development and staging projects are the right place for ZCP. Produc Most project operations do not get an extra ZCP-specific confirmation prompt. Deploys, env changes, restarts, scaling, and public-access changes are normal project operations and are audited through platform evidence. -ZCP MCP adds hard gates where the loss is not safely reversible from the conversation: - -| Operation | Gate | -| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -| Service deletion | Requires explicit same-conversation approval that names the service. Remote setup also blocks deleting the `zcp` service it is running in. | -| Wholesale service replacement after failed deploy history | Requires the agent to read failure evidence first, summarize what would be replaced, and ask for explicit confirmation. | - -Approval from an old chat does not carry forward. A new conversation needs a new approval. +ZCP MCP adds hard gates where the loss is not safely reversible from the conversation: **service deletion** (explicit same-conversation approval by service name; remote setup also blocks deleting the `zcp` service it is running in) and **wholesale service replacement after failed deploy history** (refuse-then-acknowledge with failure evidence first). Approval from an old chat does not carry forward. The full enforcement rules are in [Tokens and credentials → What ZCP enforces for destructive actions](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions). ## Audit evidence diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index 0f0b87af3..e49473183 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -15,7 +15,7 @@ Taking over is straightforward in this setup. You open the same workspace, termi ## What it includes -- **`zcp@1` workspace service.** Runs ZCP inside Zerops and gives the agent bounded operations. +- **`zcp@1` workspace service.** Runs ZCP inside Zerops and gives the agent project-scoped operations. - **Platform-injected `ZCP_API_KEY`.** Zerops injects the token into the workspace; you normally do not set it by hand. - **Bundled Claude Code when enabled.** The **Include Coding Agent** option installs and preconfigures Claude Code. You authenticate with your own Claude subscription login or API key. - **Browser VS Code when enabled.** The **Cloud IDE** option gives you a browser editor, terminal, and a place to supervise or take over the agent session. @@ -95,7 +95,7 @@ Tools installed inside the `zcp` service may see workspace environment variables | `ZCP_API_KEY` | Injected by Zerops into the `zcp` workspace. | | Agent account | Your Claude subscription login or model API credential. Zerops wires the agent to ZCP, but the agent account remains yours. | | Git credentials | Configured inside the workspace when the agent should commit or push from remote setup. | -| Production promotion | A separate production project and release process. | +| Production release | A separate production project and release process. | ## Runtime file access diff --git a/apps/docs/content/zcp/setup/local-agent-bridge.mdx b/apps/docs/content/zcp/setup/local-agent-bridge.mdx index 78a3a2e69..80fc61f2d 100644 --- a/apps/docs/content/zcp/setup/local-agent-bridge.mdx +++ b/apps/docs/content/zcp/setup/local-agent-bridge.mdx @@ -143,7 +143,7 @@ Keep `.env` out of git. It contains real connection values. ## 8. Link a deploy target -Local setup needs a Zerops runtime when the agent should deploy from your working directory. A stage runtime is the usual target because it lets local work prove itself before production promotion. +Local setup needs a Zerops runtime when the agent should deploy from your working directory. A stage runtime is the usual target because it lets local work prove itself before the production release. If there is exactly one runtime, the agent can use it automatically. If multiple runtimes exist, it should ask which one to link: diff --git a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx deleted file mode 100644 index 2508710e9..000000000 --- a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: 'Moved: Build and ship' -description: 'Development and verification are now covered in Build and ship.' ---- - -This page moved to [Build and ship](/zcp/workflows/build-with-zcp). - -- Use [Development](/zcp/workflows/build-with-zcp#develop-with-live-project-context) for app work, deploy, and verification. -- Use [How it works -> Verification has two layers](/zcp/concept/how-it-works#verification-has-two-layers) for the deeper verification model. -- Use [Troubleshooting](/zcp/reference/troubleshooting) for symptoms and recovery moves. diff --git a/apps/docs/content/zcp/workflows/build-with-zcp.mdx b/apps/docs/content/zcp/workflows/build-with-zcp.mdx index 1d8fa4270..2457ea76f 100644 --- a/apps/docs/content/zcp/workflows/build-with-zcp.mdx +++ b/apps/docs/content/zcp/workflows/build-with-zcp.mdx @@ -10,7 +10,7 @@ Build a task board. Tasks should stay saved after refresh. ``` -A prompt can be that short when the outcome is enough. Add detail when it changes behavior, architecture, stack, runtime layout, acceptance criteria, credentials, delivery, packaging, or production promotion. +A prompt can be that short when the outcome is enough. Add detail when it changes behavior, architecture, stack, runtime layout, acceptance criteria, credentials, delivery, packaging, or the production release. The expected output is a verified running change, not only generated files. The agent should prove the request against a real runtime, real managed services when used, and the logs, events, and checks that explain what happened. @@ -42,7 +42,7 @@ The expected output is a verified running change, not only generated files. The
  • Direct deploy, git, or CI
  • Optional package bundle
  • -
  • Production promotion
  • +
  • Production release
@@ -59,7 +59,7 @@ The main user-facing choice is runtime layout. Let the agent infer it from the p | Runtime layout | Use when | What to tell the agent | | ------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------- | | **Dev** | You want one mutable runtime for fast iteration, experiments, or early app work. | `Build a small Node.js API on dev.` | -| **Dev + stage** | You want a development runtime plus a separate runtime for review or promotion rehearsal. | `Build a Node.js API with PostgreSQL on dev+stage.` | +| **Dev + stage** | You want a development runtime plus a separate runtime for review or release rehearsal. | `Build a Node.js API with PostgreSQL on dev+stage.` | | **Stage / linked target** | You work from local files or a single target runtime and want the workflow to use that target. | `Use appstage as the deploy target for this local app.` | The layout is about app runtimes, not where the `zcp` binary runs. Remote setup can use any of these layouts from the `zcp@1` workspace. Local setup usually works from local files into a linked stage or app runtime. @@ -111,9 +111,9 @@ The agent may still ask which runtime to package, which half of a dev+stage pair Packaging is not the next deploy of the same app. For the next app change, keep using the normal build/deploy/verify loop. Use packaging when the output you want is a git-backed import bundle; see [Package a running service](/zcp/workflows/package-running-service). -## 5. Prepare production promotion {#prepare-production-promotion} +## 5. Prepare the production release {#prepare-production-release} -Production promotion is where authority changes. The agent prepares verified work and release evidence; production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. +The production release is where authority changes. The agent prepares verified work and release evidence; production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. The useful split is: @@ -133,6 +133,6 @@ For a completed app task, the agent should report: - the deploy or verification target, - the URL, endpoint, UI state, job result, or stored data that proves the requested behavior, - managed services, env vars, or delivery settings it touched, -- the delivery preference, packaging output, or production promotion state that now applies. +- the delivery preference, packaging output, or production release state that now applies. If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the decision or credential needed from you. diff --git a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx deleted file mode 100644 index 72297b9c7..000000000 --- a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: 'Moved: Build and ship' -description: 'Service setup is now covered in Build and ship.' ---- - -This page moved to [Build and ship](/zcp/workflows/build-with-zcp). - -- Use [Runtime layout](/zcp/workflows/build-with-zcp#choose-the-runtime-layout) for existing services, missing services, and the handoff from setup into app work. -- Use [Workflows in depth](/zcp/reference/agent-workflow) for workflow process details and exact labels. diff --git a/apps/docs/content/zcp/workflows/delivery-handoff.mdx b/apps/docs/content/zcp/workflows/delivery-handoff.mdx deleted file mode 100644 index ee47ccc3f..000000000 --- a/apps/docs/content/zcp/workflows/delivery-handoff.mdx +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: 'Moved: Build and ship' -description: 'Delivery preference is now covered in Build and ship.' ---- - -This page moved to [Build and ship](/zcp/workflows/build-with-zcp). - -- Use [Delivery after proof](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) for the user-facing choices: keep direct deploy, push verified work to git, or hand off to CI/release tooling. -- Use [Workflows in depth](/zcp/reference/agent-workflow#delivery-after-proof) for delivery labels and workflow context. diff --git a/apps/docs/content/zcp/workflows/package-running-service.mdx b/apps/docs/content/zcp/workflows/package-running-service.mdx index bfb238bf3..f3af93bc0 100644 --- a/apps/docs/content/zcp/workflows/package-running-service.mdx +++ b/apps/docs/content/zcp/workflows/package-running-service.mdx @@ -28,7 +28,7 @@ Package a runtime when you need: Do not use packaging for: - the next app deploy; use the normal build, deploy, and verify loop, -- production promotion; production needs its own infrastructure, credentials, domains, and release trigger, +- the production release; production needs its own infrastructure, credentials, domains, and release trigger, - moving managed-service data; backups and restores are separate. For production setup, follow [Promote to production](/zcp/workflows/promote-to-production). Packaging can help create a reusable app bundle, but it does not decide production infrastructure or release policy. diff --git a/apps/docs/content/zcp/workflows/promote-to-production.mdx b/apps/docs/content/zcp/workflows/promote-to-production.mdx index 621dd6c69..74aeb15b4 100644 --- a/apps/docs/content/zcp/workflows/promote-to-production.mdx +++ b/apps/docs/content/zcp/workflows/promote-to-production.mdx @@ -5,7 +5,7 @@ description: 'Move verified dev or stage work into a production Zerops project w import Icons from '@theme/Icon'; -Use this after the agent has proved work in development or stage. Production promotion is the point where authority changes: the agent can prepare proof, source changes, and setup notes, but production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. +Use this after the agent has proved work in development or stage. The production release is the point where authority changes: the agent can prepare proof, source changes, and setup notes, but production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. Production should be a separate Zerops project without a `zcp` service. That keeps the development agent out of the production blast radius while still letting verified work reach production. diff --git a/apps/docs/src/components/IntroAgentVisual/build.cjs b/apps/docs/src/components/IntroAgentVisual/build.cjs index 09690850c..3e85f885a 100644 --- a/apps/docs/src/components/IntroAgentVisual/build.cjs +++ b/apps/docs/src/components/IntroAgentVisual/build.cjs @@ -55,7 +55,7 @@ const RECIPES = [ ]; function buildLayout() { - const g = make(W, 40); + const g = make(W, 41); const logos = []; box(g, { x: 2, y: 0, w: 16, h: 8, topLabel: 'ZCP MCP' }); @@ -100,29 +100,29 @@ function buildLayout() { vline(g, 74, 18, 18); ch(g, 74, 19, '▼'); - box(g, { x: 0, y: 17, w: 60, h: 22, topLabel: 'complex project' }); + box(g, { x: 0, y: 17, w: 60, h: 23, topLabel: 'complex project' }); - box(g, { x: 3, y: 20, w: 14, h: 3, lines: ['zcp'], align: 'center', hl: true }); + box(g, { x: 3, y: 20, w: 14, h: 4, lines: ['zcp', 'Ubuntu'], align: 'center', hl: true, subtitle: true }); - box(g, { x: 3, y: 24, w: 14, h: 4, lines: ['appdev', 'Bun'], align: 'center', subtitle: true }); - box(g, { x: 23, y: 24, w: 14, h: 4, lines: ['apidev', 'Golang'], align: 'center', subtitle: true }); - box(g, { x: 43, y: 24, w: 14, h: 4, lines: ['workerdev', 'Python'], align: 'center', subtitle: true }); + box(g, { x: 3, y: 25, w: 14, h: 4, lines: ['appdev', 'Bun'], align: 'center', subtitle: true }); + box(g, { x: 23, y: 25, w: 14, h: 4, lines: ['apidev', 'Golang'], align: 'center', subtitle: true }); + box(g, { x: 43, y: 25, w: 14, h: 4, lines: ['workerdev', 'Python'], align: 'center', subtitle: true }); - box(g, { x: 3, y: 29, w: 14, h: 4, lines: ['appstage', 'Bun'], align: 'center', subtitle: true }); - box(g, { x: 23, y: 29, w: 14, h: 4, lines: ['apistage', 'Golang'], align: 'center', subtitle: true }); - box(g, { x: 43, y: 29, w: 14, h: 4, lines: ['workerstage', 'Python'], align: 'center', subtitle: true }); + box(g, { x: 3, y: 30, w: 14, h: 4, lines: ['appstage', 'Bun'], align: 'center', subtitle: true }); + box(g, { x: 23, y: 30, w: 14, h: 4, lines: ['apistage', 'Golang'], align: 'center', subtitle: true }); + box(g, { x: 43, y: 30, w: 14, h: 4, lines: ['workerstage', 'Python'], align: 'center', subtitle: true }); - box(g, { x: 3, y: 34, w: 10, h: 4, lines: ['db', 'Postgres'], align: 'center', subtitle: true }); - box(g, { x: 14, y: 34, w: 10, h: 4, lines: ['search', 'Elastic'], align: 'center', subtitle: true }); - box(g, { x: 25, y: 34, w: 10, h: 4, lines: ['broker', 'NATS'], align: 'center', subtitle: true }); - box(g, { x: 36, y: 34, w: 10, h: 4, lines: ['cache', 'Valkey'], align: 'center', subtitle: true }); - box(g, { x: 47, y: 34, w: 10, h: 4, lines: ['storage', 'S3'], align: 'center', subtitle: true }); + box(g, { x: 3, y: 35, w: 10, h: 4, lines: ['db', 'Postgres'], align: 'center', subtitle: true }); + box(g, { x: 14, y: 35, w: 10, h: 4, lines: ['search', 'Elastic'], align: 'center', subtitle: true }); + box(g, { x: 25, y: 35, w: 10, h: 4, lines: ['broker', 'NATS'], align: 'center', subtitle: true }); + box(g, { x: 36, y: 35, w: 10, h: 4, lines: ['cache', 'Valkey'], align: 'center', subtitle: true }); + box(g, { x: 47, y: 35, w: 10, h: 4, lines: ['storage', 'S3'], align: 'center', subtitle: true }); - box(g, { x: 64, y: 17, w: 22, h: 17, topLabel: 'simple project' }); + box(g, { x: 64, y: 17, w: 22, h: 18, topLabel: 'simple project' }); - box(g, { x: 69, y: 20, w: 12, h: 3, lines: ['zcp'], align: 'center', hl: true }); - box(g, { x: 69, y: 24, w: 12, h: 4, lines: ['appdev', 'Node.js'], align: 'center', subtitle: true }); - box(g, { x: 69, y: 29, w: 12, h: 4, lines: ['appstage', 'Node.js'], align: 'center', subtitle: true }); + box(g, { x: 69, y: 20, w: 12, h: 4, lines: ['zcp', 'Ubuntu'], align: 'center', hl: true, subtitle: true }); + box(g, { x: 69, y: 25, w: 12, h: 4, lines: ['appdev', 'Node.js'], align: 'center', subtitle: true }); + box(g, { x: 69, y: 30, w: 12, h: 4, lines: ['appstage', 'Node.js'], align: 'center', subtitle: true }); verify(g); return { grid: g, logos }; diff --git a/apps/docs/static/llms.txt b/apps/docs/static/llms.txt index e4c5c3297..c1d8591b5 100644 --- a/apps/docs/static/llms.txt +++ b/apps/docs/static/llms.txt @@ -337,10 +337,7 @@ - [Zcp > Setup > Choose Workspace](https://docs.zerops.io/zcp/setup/choose-workspace.md) - [Zcp > Setup > Hosted Workspace](https://docs.zerops.io/zcp/setup/hosted-workspace.md) - [Zcp > Setup > Local Agent Bridge](https://docs.zerops.io/zcp/setup/local-agent-bridge.md) -- [Zcp > Workflows > Build And Verify App](https://docs.zerops.io/zcp/workflows/build-and-verify-app.md) - [Zcp > Workflows > Build With Zcp](https://docs.zerops.io/zcp/workflows/build-with-zcp.md) -- [Zcp > Workflows > Create Or Adopt Services](https://docs.zerops.io/zcp/workflows/create-or-adopt-services.md) -- [Zcp > Workflows > Delivery Handoff](https://docs.zerops.io/zcp/workflows/delivery-handoff.md) - [Zcp > Workflows > Package Running Service](https://docs.zerops.io/zcp/workflows/package-running-service.md) - [Zcp > Workflows > Promote To Production](https://docs.zerops.io/zcp/workflows/promote-to-production.md) - [Zerops Yaml > Base List](https://docs.zerops.io/zerops-yaml/base-list.md) diff --git a/zcp-audit.md b/zcp-audit.md new file mode 100644 index 000000000..dba319d4a --- /dev/null +++ b/zcp-audit.md @@ -0,0 +1,195 @@ +# ZCP MCP reference audit (v2) + +Audit of `apps/docs/content/zcp/` (Zerops Control Plane MCP reference) for **consolidation**, **hierarchy / texture**, and **vocabulary fit to Zerops voice**. Entry point is `apps/docs/content/features/coding-agents.mdx`. Tone reference is the Zerops 3.0 article (https://zerops.io/articles/might-as-well-call-it-zerops-3-0-huge-updates-and-what-s-next). + +Date: 2026-05-13. Revised after independent review by Codex and a fresh-eyes Opus reader. Their corrections are folded into the findings below. + +--- + +## 1. Tree as it stands today + +``` +Zerops Control Plane MCP ← sidebar group headline +│ +├─ Overview 95 ln landing from /features/coding-agents +├─ Quickstart 126 ln AI Agent recipe → Claude Code → proof +├─ How it works 190 ln the loop, conceptual + mermaid +│ +├─ Remote or local setup/ ← category page = choose-workspace +│ ├─ choose-workspace 73 ln decision matrix (parent) +│ ├─ hosted-workspace 121 ln what remote gives you +│ └─ local-agent-bridge 175 ln 8-step local install +│ +├─ Build and ship/ ← category page = build-with-zcp +│ ├─ build-with-zcp 138 ln parent + §1-§5 (§4/§5 are legitimate summaries, see §6) +│ ├─ package-running-service 81 ln handoff/reuse bundle +│ └─ promote-to-production 131 ln dev → prod handoff +│ +├─ Security/ ← category page = trust-model +│ ├─ trust-model 119 ln boundary + blast radius (5 tables) +│ ├─ tokens-and-project-access 162 ln ZCP_API_KEY canonical (6 tables) +│ └─ production-policy 93 ln prod-stays-separate (3 tables) +│ +└─ Reference/ ← category page = reference/index + ├─ index 13 ln thin 4-row directory — smell, see §6 + ├─ agent-workflow 217 ln bootstrap+develop deep-dive (longest page, 9 tables) + ├─ mcp-operations 103 ln tool names (5 tables) + ├─ troubleshooting 94 ln recovery playbook (3 tables) + └─ glossary 134 ln definitions (also fed to mcp-operations as "Reference") +``` + +**Orphan stubs (not in sidebar, 9–10 lines each, redirect via prose):** +- `apps/docs/content/zcp/workflows/create-or-adopt-services.mdx` +- `apps/docs/content/zcp/workflows/delivery-handoff.mdx` +- `apps/docs/content/zcp/workflows/build-and-verify-app.mdx` + +These should become Docusaurus client redirects in `docusaurus.config.ts` or be deleted. Currently soft "moved" prose — the worst of both (still indexed, no real redirect). + +## 2. How info actually flows from /features/coding-agents + +The features page hands the reader four entry-cards: **Quickstart / Overview / Remote-or-local / Build-and-ship**. It also drops inline links to `/zcp/reference/agent-workflow`, `/zcp/reference/mcp-operations`, `/zcp/security/production-policy`. + +Reader archetypes that hit the section: + +| Reader | What they want | Where they land first | Where they get stuck | +|---|---|---|---| +| **Team lead** (adoption decision) | 90-second read: blast radius, cost, production stance | Overview | Overview re-pitches the feature instead of giving an adoption-grade summary | +| **First-time evaluator** | Try it fast, confirm the concept | Quickstart → Overview | Overview's first 30 seconds rehash the features-page intro almost verbatim | +| **Operator** (returning mid-project) | Make a decision, then execute | Build and ship | Three near-identical comparison tables across choose-workspace / trust-model / mcp-operations | +| **Recoverer** (something broke) | Get unstuck fast | Troubleshooting → Glossary | Glossary defines workflow-only terms; prose elsewhere doesn't link into it | +| **Security reviewer** | Threat model, boundaries | Trust model | Trust model and production-policy share a boundary statement but at different abstraction levels | +| **Custom MCP integrator** | Tool names, no workflow | MCP tools → Glossary | Glossary doubles as workflow term reference; integrator filters noise | + +The features page promises **"Bootstrap + Develop"** as the two phases. Overview doesn't pick that thread up — it's parked deep in `reference/agent-workflow`, which the evaluator won't reach naturally. That's the single biggest narrative break, and it's compounded by the features-page inline link (`See Workflows in depth for both`) routing readers *directly* into the most jargon-heavy reference page in the section. + +## 3. Content repetition — pattern, not policy + +The right pattern is **short local reminder at decision points + link to canonical home**, not collapse-and-redirect. Refrains are load-bearing in docs because readers land mid-section. + +Six concepts get retold three to six times. Most need only minor trims. Where I overstated in v1 is called out below. + +| Concept | Pages that touch it | Canonical home | Action | +|---|---|---|---| +| **"Proof or blocker" + URL/endpoint/UI/job/data list** | overview, quickstart, how-it-works, build-with-zcp, promote-to-production, agent-workflow, troubleshooting, glossary | **how-it-works** | Keep one-line refrains where they appear. Cut only the *full 5-bullet list* outside how-it-works; replace with link + 1-line echo | +| **Production-stays-separate policy** | overview, trust-model, tokens, production-policy, build-with-zcp §5, promote-to-production | **production-policy** | Keep a one-paragraph caveat in trust-model (it's the security parent) and a one-line callout in overview. Strip the duplicate prose from the other 3 | +| **Remote vs local comparison** | choose-workspace, how-it-works, trust-model, mcp-operations | **choose-workspace** | NOT pure duplication — each table compares different axes (decision factors / loop invariance / blast radius / tool behavior). Trim where overlap is real; keep where the axis is unique. v1 overstated this one | +| **Confirmation gates (service deletion + destructive import override)** | agent-workflow, mcp-operations, trust-model, tokens, glossary | **tokens-and-project-access** (`#what-zcp-enforces-for-destructive-actions`) | Remove duplicate table from agent-workflow, mcp-operations, trust-model — replace with link. Glossary keeps the term definition | +| **Runtime layouts (5 values)** | glossary, agent-workflow, choose-workspace (4), how-it-works (bullets), build-with-zcp (3 user-facing) | **glossary** = labels; **build-with-zcp** = user-facing 3 | Drop the duplicate 5-row table from agent-workflow | +| **Failure categories (7 values)** | glossary, agent-workflow, troubleshooting | **troubleshooting** (has "read first + avoid" columns) | agent-workflow links to it; glossary keeps the term definition | +| **Live-state inventory bullets** | overview, how-it-works, agent-workflow | **how-it-works** | Overview becomes a teaser, not a re-list | +| **ZCP_API_KEY full explanation** | tokens (canonical), production-policy, trust-model, hosted-workspace, local-agent-bridge, choose-workspace, build-with-zcp, troubleshooting | **tokens-and-project-access** | Most pages *already* describe their local concern and link out. Only **production-policy §credential-rules** and **trust-model §credential-ownership** truly re-explain the key. Trim those two; leave the rest alone | + +**Build-with-zcp §4 / §5 are NOT duplication.** They're parent-page summaries (overview-to-detail architecture). [build-with-zcp.mdx:98,112](apps/docs/content/zcp/workflows/build-with-zcp.mdx#L98) link to the detail pages; [package-running-service.mdx:50,63](apps/docs/content/zcp/workflows/package-running-service.mdx#L50) carry env classification and exclusions the summary doesn't have. v1 called this two-place truth; it isn't. Keep the summaries as 3-sentence anchored deep-links — parent pages that only link out feel hollow. + +**Before sweep 2: quantify repetition vs unique content per page.** If `trust-model.mdx` is 60% unique threat-model content and 40% restated policy, cutting the 40% is safe. Inverse, the page collapses. Worth measuring before swinging the axe. + +## 4. Off-brand vocabulary vs Zerops 3.0 voice + +The Zerops 3.0 article voice uses: *deploy, managed services, environment parity, productized DevOps knowledge, topology, transparent by default, sensible defaults, hand off production-ready code*. It avoids: *seamless, intuitive, best practices, orchestration, promotion (noun)*. + +| Off-brand term | Where it appears | Why it grates | Direction | +|---|---|---|---| +| **"Production promotion"** (noun) | glossary, build-with-zcp §5, package-running-service, quickstart card, promote-to-production prose | Article says "hand off production-ready code." Verb page (`promote-to-production`) is fine. The noun is invented jargon | **"Production release"** as the noun. "Handoff" is already overloaded (delivery handoff, human handoff, external handoff); adding "production handoff" makes a 4th meaning. Verb pages already say "release process" | +| **"Operating loop"** | overview, how-it-works description | Features page already uses **"the short work loop"** and **"develop loop"** — both more concrete | **"work loop"** or **"develop loop"** consistently | +| **"Workflow-guided"** (adjective) | overview, reference/index, agent-workflow, mcp-operations | Jargon-y compound. Article doesn't need a name for "agents that follow our instructions" | Drop the adjective. Where context needs it: *"a run that follows the generated workflow"* or just *"a guided run"* | +| **"Bounded operations"** | overview, glossary, hosted-workspace | Abstract. **"Project-scoped operations"** is also used and is more concrete | Standardise on **"project-scoped operations"** | +| **"Opinionated"** | how-it-works ("workflow is opinionated about...") | Industry meme word the article voice avoids. Single occurrence | Replace with *"the workflow handles..."* / *"the workflow decides..."* | +| **"Concerns" / "Done criteria" / "Done gate"** | how-it-works | Agile-speak | *"things the workflow handles"*, *"what counts as done"*, *"the proof gate"* | +| **"Setup" overload** | Everywhere | Six meanings: Remote setup, Local setup, AI Agent setup (recipe env), Service setup (workflow phase), `setup:` block (zerops.yaml), Bootstrap setup | "Remote workspace" / "Local workspace" already exist in places — push that distinction harder. Leave "setup" for the workflow phase and the YAML key | +| **"Blast radius"** | trust-model, choose-workspace, tokens, production-policy, features page | Used consistently. Aligns with article tone | **Keep** | + +**v1 got "Bootstrap" wrong — dropped.** The features page brands `Bootstrap + Develop` as a labelled pair at [coding-agents.mdx:66,120](apps/docs/content/features/coding-agents.mdx#L66). Renaming to "Service setup" breaks the symmetric pair (mismatched grammar and abstraction level). The real problem is one apologetic sentence at [agent-workflow.mdx:15](apps/docs/content/zcp/reference/agent-workflow.mdx#L15): *"Bootstrap is not a marketing or onboarding step. It is the workflow's name for..."* — that line is what reveals the term as defensive. **Delete the apology, keep the label.** + +**Vocabulary lives in both ZCP and features.** "Bootstrap" appears in [coding-agents.mdx:66,120](apps/docs/content/features/coding-agents.mdx#L66); any vocabulary pass has to touch the features page too, not just `/zcp/`. + +## 5. Hierarchy / uniformity — abstraction collision, not just texture + +**Every page has the same skeleton:** 1-paragraph intro → "What you're choosing / what it does" H2 → 2–6 tables → "Related" / "Next steps." Result reads flat regardless of whether you're on a security threat model or a CLI install guide. + +**Table counts per page (actual, recounted after Codex pushback):** + +| Page | Tables | Diagnosis | +|---|---|---| +| reference/agent-workflow | **9** | Worst offender. Trim to 2–3; turn duplicated tables (runtime layouts, failure categories, confirmation gates) into prose with anchors to canonical homes | +| security/tokens-and-project-access | **6** | Credential map + rejected tokens + rotation justify tables; rest can be prose | +| reference/mcp-operations | **5** | A tool reference IS table-shaped — keep, but vary widths/styles | +| security/trust-model | **5** | Blast-radius table earns its place; rest are repeating credentials/rules | +| concept/how-it-works | **4** + 1 mermaid | The mermaid earns its keep; tables compete with it | +| workflows/build-with-zcp | **3** | OK | +| reference/troubleshooting | **3** | OK — playbook genuinely needs tables | + +**Texture moves missing across the board:** +- Almost no `:::tip` / `:::caution` callouts (only overview has one) +- ASCII visual / diagrams used ONLY in how-it-works + agent-workflow (mermaid) +- No definition lists outside glossary +- Highlighted-span class `.ascii-graph__highlight` built for features page isn't reused +- No inline code-snippet examples on most security/reference pages +- **Glossary terms aren't linked from prose.** Across the section, `appdev`, `appstage`, `local-stage`, `delivery mode`, `runtime layout` appear without anchor links to glossary entries. That's the real fix for "glossary doing two jobs": let prose carry meaning, let glossary be the index + +**Same-shape problem:** [concept/how-it-works.mdx](apps/docs/content/zcp/concept/how-it-works.mdx) (190 lines) and [reference/agent-workflow.mdx](apps/docs/content/zcp/reference/agent-workflow.mdx) (217 lines) tell the same story at adjacent abstraction levels — same two-phase split, same mermaid logic, same vocabulary. The deeper issue isn't visual sameness; it's **level-of-abstraction collision**. Concept page should teach the *idea*; reference should catalog *exact labels*. They blur. Merging is the right instinct, but commit to a hard line-budget — a merged 350-line page that no archetype reads end-to-end is worse than the split. + +## 6. Structural problems + +1. **Three orphan stubs** still live in the content tree — they should be deleted or converted to real Docusaurus redirects. Zero-risk fix; ship before anything else. + +2. **`reference/index.mdx` is 13 lines.** Either richer (mini-toc with use-cases, when-to-reach-for-each, common combinations) or absent (collapse into sidebar group with no landing page). A 13-line landing for a category is a smell. + +3. **Reference subsection is over-stocked at the wrong abstraction.** [agent-workflow.mdx](apps/docs/content/zcp/reference/agent-workflow.mdx) (217 lines) is the longest page in the section but it's filed under Reference, where users won't look. Worse: the features page's inline link (`See Workflows in depth`) drops evaluators directly into it — broken information scent. + +4. **Glossary is doing two jobs**: legitimate term dictionary + canonical home for confirmation gates / failure categories / runtime layouts. Non-term entries should live on the operational pages; glossary should be a thin dictionary. Fix in tandem with linking glossary terms from prose (§5). + +5. **"Start here" cards repeat across three pages** with overlapping items. [overview.mdx](apps/docs/content/zcp/overview.mdx) "Start here" tables, [quickstart.mdx](apps/docs/content/zcp/quickstart.mdx) "Next steps" DocCardList, [coding-agents.mdx](apps/docs/content/features/coding-agents.mdx) "Where to start" cards — three nav surfaces showing 60% overlapping links. Navigation duplication, not content duplication, but the reader hits it three times in 5 minutes. + +6. **Overview's first 30 seconds are weak.** Lines 8–12 of [overview.mdx](apps/docs/content/zcp/overview.mdx#L8) restate the features-page intro almost verbatim ("Zerops provides... ZCP MCP is the project-scoped tool layer..."). Overview should *start* with the public-preview caveat + a 3-bullet "what's in this section" map, not re-pitch the feature. + +7. **Factual narrowing on the features page.** [coding-agents.mdx:87](apps/docs/content/features/coding-agents.mdx#L87) says the agent "opens a PR" before production. [promote-to-production.mdx:64](apps/docs/content/zcp/workflows/promote-to-production.mdx#L64) lists five equally valid paths (tag push, protected branch merge, manual CLI, PR, CI job). Features page narrows a real choice — readers form an expectation the workflow page contradicts. + +8. **Anchor reliability not verified.** Audit recommends linking to canonical anchors (e.g. `tokens-and-project-access#what-zcp-enforces-for-destructive-actions`). That anchor exists; not all proposed targets do. Verify before edits cite them. + +## 7. Proposed sweeps — revised order + +Original v1 ordering (vocabulary → consolidation → hierarchy) was wrong for rework cost. Vocab touches every page; if you do it first and then merge `how-it-works` ↔ `agent-workflow` later, you redo the vocab work inside the merge. + +Better order: **set the surface, then operate on it.** + +### Sweep 1 — Structural deletions (30 min, zero content risk) +- Delete or redirect the 3 orphan stubs (Docusaurus client redirects in `docusaurus.config.ts`) +- Decide whether `reference/index.mdx` becomes richer or disappears +- Fix the factual narrowing in [coding-agents.mdx:87](apps/docs/content/features/coding-agents.mdx#L87) — "opens a PR" → reflect the real range of paths +- Verify all anchor targets that downstream sweeps will cite + +### Sweep 2 — Hierarchy decisions (sets the surface) +- Decide: merge `how-it-works` ↔ `agent-workflow` (target ≤200 lines combined), or sharpen the abstraction split between them? +- Decide: keep `build-with-zcp` §4/§5 as 3-sentence anchored summaries, or remove? +- Trim navigation duplication across overview / quickstart / coding-agents "Start here" cards — pick one canonical entry-card set per audience +- Rewrite overview's first 30 seconds: drop the re-pitch, lead with "what's in this section" + +### Sweep 3 — Consolidation (on the now-smaller surface) +- Apply the local-reminder-plus-link pattern from §3: + - Trim full proof/blocker list to one place, keep refrains + - Strip duplicate confirmation-gates table from 3 pages + - Strip duplicate failure-categories and runtime-layouts tables from agent-workflow + - Cut the ZCP_API_KEY re-explanation from `production-policy` and `trust-model` only + - Trim where remote/local tables overlap; keep the axes that are genuinely different +- Link glossary terms from prose across the section + +### Sweep 4 — Vocabulary (find-and-replace over stable surface) +- "Production promotion" (noun) → "production release" +- "Operating loop" → "work loop" / "develop loop" +- "Workflow-guided" → drop or rephrase +- "Bounded operations" → "project-scoped operations" +- "Opinionated", "concerns", "done gate" → concrete replacements +- Delete the apologetic Bootstrap line at [agent-workflow.mdx:15](apps/docs/content/zcp/reference/agent-workflow.mdx#L15); keep the label +- Push "Remote workspace" / "Local workspace" disambiguation where setup-overload bites +- Touch the features page for vocab too (Bootstrap stays, but "Operating loop" / "workflow-guided" / etc may need fixing there) + +**Run as one editorial pass with four subgoals, not four separate review cycles.** Three or four cycles invite drift between them. + +## 8. Risks worth flagging before any edits + +- **Killing "Bootstrap" was wrong** (already cut from the plan above). External surfaces — recipe READMEs, blog posts, community threads — may reference Bootstrap as the phase name. Grep the codebase for external mentions before any aggressive vocab change. +- **Collapsing production-policy prose** risks the security reviewer's read path. They land on `trust-model.mdx` first (security sidebar parent at [sidebars.js:667](apps/docs/sidebars.js#L667)). Stripping the production caveat from there leaves the threat model without its most important boundary statement. Keep a one-paragraph reminder. +- **Removing `build-with-zcp` §4/§5 summaries** breaks the page as a standalone read for the operator archetype who wants the whole pipeline in one place. Keep as anchored 3-sentence summaries. +- **Merging how-it-works + agent-workflow** risks a 350-line monster. ≤200-line target or don't merge. +- **"Production release" as the replacement noun** is cleaner than "handoff" but verify it doesn't collide with anything else in the docs before global replace. +- **Quantify before sweep 3.** Measure repetition vs unique content per page. Pages where 60%+ is unique can survive a 40% trim; the inverse can't. From b1ecd106fc67624815e15724d987cb9e01be9e6e Mon Sep 17 00:00:00 2001 From: Ales Rechtorik Date: Wed, 13 May 2026 13:03:29 +0200 Subject: [PATCH 22/24] Pin JetBrains Mono for ASCII diagrams to fix cross-platform misalignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AsciiGraph and IntroAgentVisual components rely on every glyph (letters, digits, spaces, box-drawing characters │ ─ ▼ └ ┘) being exactly 1ch wide. With the previous --ifm-font-family-monospace stack, browsers were falling back to a different font for box-drawing characters on macOS, Windows, and especially Android — drifting the diagrams off-grid. - Add self-hosted JetBrains Mono Regular woff2 under static/fonts/. - Preload the font in headTags so the first paint is already on-grid (no FOUT/CLS visible to the reader). - Introduce --ascii-graph-font with JetBrains Mono as the lead, system monospace as fallback; apply it to .ascii-graph and .intro-ascii. - Disable contextual alternates and ligatures on those elements so adjacent glyphs cannot collapse or shift. Code blocks elsewhere on the site keep the default monospace stack; this change is scoped to the ASCII diagrams that need a strict grid. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/docs/docusaurus.config.js | 12 ++++++++ apps/docs/src/css/custom.css | 26 ++++++++++++++++-- .../static/fonts/JetBrainsMono-Regular.woff2 | Bin 0 -> 92380 bytes 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 apps/docs/static/fonts/JetBrainsMono-Regular.woff2 diff --git a/apps/docs/docusaurus.config.js b/apps/docs/docusaurus.config.js index 114d0615f..293aee9d9 100644 --- a/apps/docs/docusaurus.config.js +++ b/apps/docs/docusaurus.config.js @@ -17,6 +17,18 @@ const config = { favicon: "favicon.ico", organizationName: "zerops", projectName: "zerops/docs", + headTags: [ + { + tagName: "link", + attributes: { + rel: "preload", + href: "/fonts/JetBrainsMono-Regular.woff2", + as: "font", + type: "font/woff2", + crossorigin: "anonymous", + }, + }, + ], plugins: [ './src/plugins/markdown-source', require.resolve("docusaurus-plugin-image-zoom"), diff --git a/apps/docs/src/css/custom.css b/apps/docs/src/css/custom.css index 8b3bc0a87..2bfb0e819 100644 --- a/apps/docs/src/css/custom.css +++ b/apps/docs/src/css/custom.css @@ -1220,14 +1220,34 @@ html[data-theme='dark'] .zcp-feature-card p { color: #cbd5e1; } /* AsciiGraph — blended with page, no code-block chrome */ +/* Pinned webfont so box-drawing characters (│ ─ ▼ └ ┘) line up at exactly + 1ch on every platform. The system-monospace fallbacks (SF Mono, Consolas, + Roboto Mono) substitute a different font for those glyphs and the diagrams + drift, badly on Android. */ +@font-face { + font-family: 'JetBrains Mono'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('/fonts/JetBrainsMono-Regular.woff2') format('woff2'); +} + +:root { + --ascii-graph-font: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', 'Courier New', monospace; +} + .ascii-graph { background: transparent; border: none; color: #475569; display: block; - font-family: var(--ifm-font-family-monospace); + font-family: var(--ascii-graph-font); font-size: 0.88rem; line-height: 1.4; + /* Disable contextual alternates / ligatures so adjacent glyphs never + collapse or shift. */ + font-variant-ligatures: none; + font-feature-settings: 'liga' 0, 'calt' 0; margin: 2.5rem auto 3rem; max-width: 100%; overflow-x: auto; @@ -1320,9 +1340,11 @@ html[data-theme='dark'] .ascii-graph__highlight { overflow-x: auto; /* Lock font + line-height to .ascii-graph values so that 1ch / 1lh on absolutely-positioned children align with the pre's character grid. */ - font-family: var(--ifm-font-family-monospace); + font-family: var(--ascii-graph-font); font-size: 0.88rem; line-height: 1.4; + font-variant-ligatures: none; + font-feature-settings: 'liga' 0, 'calt' 0; } .intro-ascii__frame { diff --git a/apps/docs/static/fonts/JetBrainsMono-Regular.woff2 b/apps/docs/static/fonts/JetBrainsMono-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..66c54672c787c0756adf28d5823b4944009a8701 GIT binary patch literal 92380 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

P>xbtSQ;#ONFb3&n~Y5f01Pu?0WQGI?Mhm% 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 Date: Wed, 13 May 2026 13:05:12 +0200 Subject: [PATCH 23/24] =?UTF-8?q?Fix=20off-by-one=20in=20develop=20loop=20?= =?UTF-8?q?ASCII:=20=E2=94=90=20was=20one=20column=20shy=20of=20=E2=94=98?= =?UTF-8?q?=20and=20=E2=94=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The top-right corner of the loop-back arrow sat at column 50; every │ below it and the ┘ at the bottom sat at column 51. Added one more ─ between ◀ and ┐ so the corner aligns with the vertical line. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/docs/content/features/coding-agents.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx index 715091126..6e8efb8ae 100644 --- a/apps/docs/content/features/coding-agents.mdx +++ b/apps/docs/content/features/coding-agents.mdx @@ -68,7 +68,7 @@ The agent runs through two workflows. **Bootstrap** settles which services and r {` `}{`zcp (local or remote)`}{` │ ▼ - edit code on `}{`appdev`}{` ◀───────────────────────┐ + edit code on `}{`appdev`}{` ◀────────────────────────┐ └ dev server, hot reload │ │ │ ▼ │ From 47451233c3ad4afd4559b3444edb944162a7e808 Mon Sep 17 00:00:00 2001 From: Ales Rechtorik Date: Thu, 14 May 2026 00:09:37 +0200 Subject: [PATCH 24/24] Apply card-grid layouts to ZCP overview and reference indexes - Replace the routing table on the reference index with a DocCardList and convert "Common combinations" bullets into scenario cards. - Replace the section-nav bullets and "What the agent gets" paragraphs on the overview with categorized card grids; titles name what's behind each card instead of being evocative. - Reorder the Features sidebar so Local & Remote Development sits below Access & Networking, and Infrastructure for Coding Agents sits below Projects, Services & Containers. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/docs/content/zcp/overview.mdx | 91 +++++- apps/docs/content/zcp/reference/index.mdx | 84 ++++- apps/docs/sidebars.js | 36 +-- apps/docs/static/llms-full.txt | 378 ++++++++-------------- apps/docs/static/llms-small.txt | 378 ++++++++-------------- 5 files changed, 458 insertions(+), 509 deletions(-) diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index ddaa5138f..fbaf5ebe0 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -11,10 +11,49 @@ ZCP MCP is a public preview and is under active development. If you hit a bug, c This section covers, in order: -- **How a run unfolds.** [Quickstart](/zcp/quickstart) for hands-on; [How it works](/zcp/concept/how-it-works) for the work loop. -- **Where the agent runs.** [Remote or local setup](/zcp/setup/choose-workspace), [Trust model](/zcp/security/trust-model), and [Production boundary](/zcp/security/production-policy). -- **What you decide while working.** [Build and ship](/zcp/workflows/build-with-zcp), [Package a running service](/zcp/workflows/package-running-service), [Promote to production](/zcp/workflows/promote-to-production). -- **Exact labels when they matter.** [Workflows in depth](/zcp/reference/agent-workflow), [ZCP MCP tools](/zcp/reference/mcp-operations), [Troubleshooting](/zcp/reference/troubleshooting), [Glossary](/zcp/glossary). +

+ +
+ +GET STARTED + +

Quickstart and concept

+ +[Quickstart](/zcp/quickstart) for hands-on; [How it works](/zcp/concept/how-it-works) for the work loop. + +
+ +
+ +SETUP + +

Remote or local workspace

+ +[Remote or local setup](/zcp/setup/choose-workspace), [Trust model](/zcp/security/trust-model), and [Production boundary](/zcp/security/production-policy). + +
+ +
+ +WORKFLOWS + +

Build, package, promote

+ +[Build and ship](/zcp/workflows/build-with-zcp), [Package a running service](/zcp/workflows/package-running-service), [Promote to production](/zcp/workflows/promote-to-production). + +
+ +
+ +REFERENCE + +

Troubleshooting and lookup

+ +[Workflows in depth](/zcp/reference/agent-workflow), [ZCP MCP tools](/zcp/reference/mcp-operations), [Troubleshooting](/zcp/reference/troubleshooting), [Glossary](/zcp/glossary). + +
+ +
For the broader feature concept — why coding agents need real project infrastructure rather than a sandbox or generated artifact — start with [Infrastructure for Coding Agents](/features/coding-agents). @@ -24,13 +63,49 @@ Your prompt can stay about the product. Name the stack, runtime layout, acceptan ## What the agent gets -**Current state.** Services, runtime layout, managed dependencies, env-var keys and references, logs, events, deploy history, verification state, and saved work state. +
+ +
+ +STATE + +### Current state + +Services, runtime layout, managed dependencies, env-var keys and references, logs, events, deploy history, verification state, and saved work state. + +
+ +
+ +CONTROLS + +### Zerops operations + +Project-scoped tools for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. + +
+ +
+ +INSTRUCTIONS + +### Workflow + +The generated instructions combine service setup and app development: inspect state, choose the runtime target, use or create services, wire code and `zerops.yaml`, deploy, verify, and choose delivery. The Zerops work stays behind the product task instead of becoming another checklist. + +
+ +
+ +EVIDENCE + +### Evidence-based completion -**Zerops operations.** Project-scoped tools for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. +A build or deploy is not the finish line. A completed app task should end with a working URL, endpoint result, UI proof, or a blocker backed by logs, events, and verification evidence. -**Workflow.** The generated instructions combine service setup and app development: inspect state, choose the runtime target, use or create services, wire code and `zerops.yaml`, deploy, verify, and choose delivery. The Zerops work stays behind the product task instead of becoming another checklist. +
-**Evidence-based completion.** A build or deploy is not the finish line. A completed app task should end with a working URL, endpoint result, UI proof, or a blocker backed by logs, events, and verification evidence. +
## What you no longer have to script diff --git a/apps/docs/content/zcp/reference/index.mdx b/apps/docs/content/zcp/reference/index.mdx index c83795b4c..e11240461 100644 --- a/apps/docs/content/zcp/reference/index.mdx +++ b/apps/docs/content/zcp/reference/index.mdx @@ -3,19 +3,81 @@ title: "Reference" description: "Process details, MCP tool names, recovery guidance, and glossary for ZCP MCP." --- +import Icons from '@theme/Icon'; + Reference pages exist for the moments when an exact label, tool name, or recovery step matters. For day-to-day app work, start in [Build and ship](/zcp/workflows/build-with-zcp); for the loop itself, [How it works](/zcp/concept/how-it-works). -## When to reach for which page + + +## Common scenarios + +
+ +
+ +SCENARIO + +### Debugging a stuck deploy + +[Troubleshooting → Evidence order](/zcp/reference/troubleshooting#evidence-order) names what to read first; [Workflows in depth → Failure categories](/zcp/reference/agent-workflow#failure-categories) maps the labels. + +
+ +
+ +SCENARIO + +### Building a custom MCP client + +[ZCP MCP tools](/zcp/reference/mcp-operations) for the tool surface; [Workflows in depth → What drives the workflow](/zcp/reference/agent-workflow#what-drives-the-workflow) for what the generated workflow adds on top of bare tools. + +
+ +
+ +SCENARIO + +### Auditing a finished run -| Reading because… | Read | Sibling page you usually want next | -|---|---|---| -| You want the catalog of phases, routes, layouts, and labels behind a generated-workflow run | [Workflows in depth](/zcp/reference/agent-workflow) | [Glossary](/zcp/glossary) for definitions, [Troubleshooting](/zcp/reference/troubleshooting) for stuck runs | -| You are using ZCP MCP from a custom client, or you need exact tool names for agent policy | [ZCP MCP tools](/zcp/reference/mcp-operations) | [Tokens and credentials](/zcp/security/tokens-and-project-access) for what a token allows | -| The agent is stuck, a session was interrupted, deploys keep failing, or you are taking over manually | [Troubleshooting](/zcp/reference/troubleshooting) | [Workflows in depth](/zcp/reference/agent-workflow) for the labels evidence will surface | -| A page, workflow status, or handoff used a term you want to pin down (`appdev`, `local-stage`, `delivery mode`, etc.) | [Glossary](/zcp/glossary) | The page that referenced the term | +[Workflows in depth → Completion evidence](/zcp/reference/agent-workflow#completion-evidence) for what should be in the final answer; [Trust model → Audit evidence](/zcp/security/trust-model#audit-evidence) for what Zerops records platform-side. -## Common combinations +
-- **Debugging a stuck deploy.** [Troubleshooting → Evidence order](/zcp/reference/troubleshooting#evidence-order) names what to read first; [Workflows in depth → Failure categories](/zcp/reference/agent-workflow#failure-categories) maps the labels. -- **Building a custom MCP client.** [ZCP MCP tools](/zcp/reference/mcp-operations) for the tool surface; [Workflows in depth → What drives the workflow](/zcp/reference/agent-workflow#what-drives-the-workflow) for what the generated workflow adds on top of bare tools. -- **Auditing a finished run.** [Workflows in depth → Completion evidence](/zcp/reference/agent-workflow#completion-evidence) for what should be in the final answer; [Trust model → Audit evidence](/zcp/security/trust-model#audit-evidence) for what Zerops records platform-side. +
diff --git a/apps/docs/sidebars.js b/apps/docs/sidebars.js index e5bbcabab..a95176616 100644 --- a/apps/docs/sidebars.js +++ b/apps/docs/sidebars.js @@ -56,6 +56,15 @@ module.exports = { }, ], }, + { + type: 'doc', + id: 'features/coding-agents', + label: 'Infrastructure for Coding Agents', + customProps: { + sidebar_icon: 'sparkles', + }, + className: 'homepage-sidebar-item', + }, { type: 'category', link: { @@ -104,6 +113,15 @@ 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/rbac', @@ -140,24 +158,6 @@ 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', diff --git a/apps/docs/static/llms-full.txt b/apps/docs/static/llms-full.txt index 729ec7700..5bb32a4af 100644 --- a/apps/docs/static/llms-full.txt +++ b/apps/docs/static/llms-full.txt @@ -9311,18 +9311,18 @@ Remember that only publicly accessible objects will be cached by the CDN. Privat # Features > Coding Agents -Zerops was built on the idea of **environment parity** — to give developers the whole development lifecycle, from remote development to highly available production, with all the observability and developer tools for maximum flexibility, while keeping the configs reasonable. And it turns out that this is **exactly what coding agents need** to produce and iterate on production-ready applications. +Zerops was built on the idea of **environment parity** — giving developers the full development lifecycle, from remote development to highly available production, with the observability and developer tools for maximum flexibility, and sensible defaults so the configs stay reasonable. Turns out that's **exactly what coding agents need** to produce and iterate on production-ready applications. ## What is it -An **[MCP server](/zcp/overview)** for your agent that can run locally or on a remote container that makes use of the flexibility and processes Zerops allows. There is no "Zerops system prompt" hogging your context window. The MCP server is a thin layer that makes **your agent a Zerops platform power user** — when needed — so it understands: +An **[MCP server](/zcp/overview)** for your agents — with an optional remote cloud development environment container to run them in — that makes use of the flexibility and processes Zerops provides. There's no "Zerops system prompt" hogging your context window. The MCP server is a thin layer that makes **your agent a Zerops platform power user** — when needed — so it understands: - how networking, scaling, debugging, build & deployment, environment variables, and service provisioning work on the platform - the development loop: start a dev server → implement → deploy to stage → verify -It's the depth and flexibility of the platform that then give the agent its superpowers. A single agent can scaffold and iterate on projects with multiple runtimes (`app`, `api`, `worker`) backed by multiple [managed services](/features/infrastructure) (object storage `storage`, Postgres `db`, Elasticsearch `search`, NATS `broker`, Valkey `cache`) — end-to-end coverage from development to highly available production — just as easily as it can work on a simple Next.js presentational website. +The depth of the platform underneath is what gives the agent room to actually work. A single agent can scaffold and iterate on projects with multiple runtimes (`app`, `api`, `worker`) backed by multiple [managed services](/features/infrastructure) (object storage `storage`, Postgres `db`, Elasticsearch `search`, NATS `broker`, Valkey `cache`) — just as easily as it can work on a simple Next.js presentational website. -A **[recipe ecosystem](https://app.zerops.io/recipes)** covering the most popular frameworks — pre-prepared for the whole lifecycle — gives the agent a baseline to start from, or a guide for setting up any given stack end-to-end on Zerops. +A **[recipe ecosystem](https://app.zerops.io/recipes)** covering the most popular frameworks — pre-prepared for the whole lifecycle — gives the agent a baseline to start from, or a guide for setting up any stack on Zerops. ```text ┌─ ZCP MCP ────┐ ┌─ recipes for any stack ────────────────────────────────┐ @@ -9347,6 +9347,7 @@ A **[recipe ecosystem](https://app.zerops.io/recipes)** covering the most popula │ ▼ │ │ ▼ │ │ ┌────────────┐ │ │ ┌──────────┐ │ │ │ zcp │ │ │ │ zcp │ │ +│ │ Ubuntu │ │ │ │ Ubuntu │ │ │ └────────────┘ │ │ └──────────┘ │ │ │ │ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ ┌──────────┐ │ @@ -9375,21 +9376,21 @@ Most coding-agent platforms went agent-first, then bolted on infrastructure — ## Main features -### Bring your own agent and your own subscription +### Your agent, your subscription -Currently Claude Code, with Codex, Gemini CLI, opencode, and any other MCP-capable agent coming soon. Modify the full Linux Ubuntu container the agent runs on; configure the agent your way. **Zerops doesn't resell tokens** — sign in with your own subscription. We just provide the infrastructure to run the coding agents and what they build. +Bring whichever agent you already use. Claude Code today; Codex, Gemini CLI, opencode, and any MCP-capable client next. The container is a regular Ubuntu — install your own CLI tools, drop in your `.claude` / `.cursor` configs, attach your IDE over SSH. **Zerops doesn't resell tokens or proxy your model calls** — sign in with your own Anthropic / OpenAI / Google subscription and the agent talks to its provider directly. -### Run locally or remote, access however you want +### An ordinary Zerops project underneath -The **[`zcp` service](/zcp/setup/hosted-workspace)** — a `zcp@1` Linux container the agent can live inside, on the project's private network, with a bundled coding agent CLI and Browser VS Code as options. The **[`zcp` MCP](/zcp/setup/local-agent-bridge)** — an MCP server that exposes one project's Zerops operations through bounded, tagged tools that any MCP-capable agent client can call. Use one, the other, or both. The project surface stays the same. +The agent operates inside a normal Zerops project — same shape as production. Managed databases (PostgreSQL, MariaDB, ClickHouse), key-value stores (KeyDB, Valkey), search (Elasticsearch, Meilisearch, Typesense), vector store (Qdrant), message queues (NATS, Kafka), object and shared storage, managed Nginx — all on a private network, addressable by hostname. The same pipeline that deploys here can deploy to a separate HA production project with no `zcp` service attached. Not a sandbox the work outgrows. -### Seamless handover between human and AI +### Human ↔ agent handover -The agent runs on the same cloud development environment a human would use, and it operates on it just as a human developer would. The human can jump in at any time through any access — Cloud IDE, your local IDE over SSH, or your terminal. +The agent and the human share the same workspace — same filesystem, same SSH key, same Cloud IDE in the browser, same remote-dev attach from your local IDE. Take over mid-session, set something up before the agent runs, debug in the same container the agent just left. Nothing reaches production until you merge the PR — the agent opens it, you gate it. -### Use any technological stack you want +### Recipes for any stack -Zerops provides a curated set of recipes for most popular frameworks and programming languages. Pick one, select the **AI Agent** environment, and within minutes you have a fully working setup: Cloud IDE, authorized coding agent, and Zerops knowledge so the agent knows exactly how to develop, debug, and deploy on it. +Runtimes for Bun, Deno, Node.js, Go, Python, Rust, Java, .NET, PHP, Elixir, Gleam — with curated recipes for the framework on top: Next.js, Nuxt, Astro, Svelte, React, Vue, Angular, Solid, Qwik, Analog, NestJS, Laravel, and more. Pick a recipe, select the **AI Agent** environment, and the project comes up with a working dev runtime, a stage runtime, the right managed services wired in, and a coding agent briefed on the Zerops surface. ## How it works in practice @@ -9492,7 +9493,7 @@ The agent runs through two workflows. **Bootstrap** settles which services and r zcp (local or remote) │ ▼ - edit code on appdev ◀───────────────────────┐ + edit code on appdev ◀────────────────────────┐ └ dev server, hot reload │ │ │ ▼ │ @@ -9509,7 +9510,7 @@ The agent runs through two workflows. **Bootstrap** settles which services and r URL (proof) fix from evidence ─────┘ ``` -Once the loop produces verified work, the agent doesn't push to production — it opens a PR from the dev project. **A human review gates the merge**, then a release tag or merge triggers automatic CI/CD into a [separate production project](/zcp/security/production-policy) that has no `zcp` service at all. +Once the loop produces verified work, the agent doesn't push to production. It hands off — through whichever path your team uses: a tag push, a protected-branch merge, a manual `zcli` release, a pull request, or a CI job. **A human gates the release**, and the production deploy runs into a [separate production project](/zcp/security/production-policy) that has no `zcp` service at all. ```text Dev project GitHub Prod project @@ -9529,18 +9530,6 @@ Once the loop produces verified work, the agent doesn't push to production — i With the model clear, here's where ZCP sits among adjacent tools. People say "agent infrastructure," "infra for Claude agents," "agent hosting," or "agent sandboxes" to mean different things — usually one of the categories below. -#### Local coding agents - -_Cursor · Windsurf · Zed · Claude Code · Codex CLI · Aider · Cline_ - -The agent runs on your machine, edits your local checkout, runs local commands. Excellent at code changes. Anything beyond your machine — databases, networking, deploy targets, runtime logs — is yours to wire in. Install the ZCP MCP locally and run `zcli vpn up`, and any of these agents now reaches a real Zerops project alongside your local code: services addressed by hostname, deploys, logs, events. - -#### Cloud dev environments with AI - -_GitHub Codespaces + Copilot Workspace · Gitpod · Coder_ - -A managed Linux container in the cloud with your editor and an AI assistant attached. Solves "where the agent runs" so state survives between sessions and machine setup stops being a question. The `zcp` service is the closest thing in this category — managed Linux container, Cloud IDE, your agent of choice — except it lives *inside* a Zerops project, addressing the project's runtimes, database, and cache by hostname, with deploys and logs as primitives the agent can call. - #### PaaS MCPs _AWS MCP servers · Railway MCP · Fly.io MCP · Render MCP_ @@ -9551,13 +9540,25 @@ Most major PaaS providers have shipped an MCP — provision services, deploy cod _Hetzner Cloud MCP · DigitalOcean MCP · Linode MCP · raw SSH/server MCPs_ -"Let your agent manage your Hetzner VPS." An MCP gives the agent access to a Linux box — provision it, SSH in, install Docker and Postgres and nginx, set up systemd units, point a domain at it. Maximum control, lowest cost, full Linux to play with. The same reason senior developers reach for managed platforms applies here: even if you *can* harden SSH, write nginx configs, tune `pg_hba.conf`, rotate TLS, set firewall rules, run fail2ban, and stay on top of CVEs across kernel, OpenSSL, Docker, Postgres, and nginx — you usually don't want to. Each layer is its own domain with its own gotchas, each is a security surface that drifts the moment you stop tending it, and every project becomes a snowflake. Hand that work to an agent and you also hand it the blast radius: misconfigured firewall, debug endpoint left open, port 5432 exposed to the internet, keys never rotated. ZCP is the inverse trade-off — the **guardrails** of a managed platform. Private networking, TLS, isolated services, safe defaults, managed backups, no exposed ports unless you opt in — all built in. The agent operates through [bounded MCP tools](/zcp/reference/mcp-operations) instead of `sudo` on a fresh Ubuntu box, so the mistakes that matter on a VPS aren't reachable from where it sits. +"Let your agent manage your Hetzner VPS." An MCP gives the agent access to a Linux box — provision it, SSH in, install Docker and Postgres and nginx, set up systemd units, point a domain at it. Maximum control, lowest cost, full Linux to play with. The same reason senior developers reach for managed platforms applies here: even if you *can* harden SSH, write nginx configs, tune `pg_hba.conf`, rotate TLS, set firewall rules, run fail2ban, and stay on top of CVEs across kernel, OpenSSL, Docker, Postgres, and nginx — you usually don't want to. Each layer is its own domain with its own gotchas, each is a security surface that drifts the moment you stop tending it, and every project becomes a snowflake. Hand that work to an agent and you also hand it the blast radius: misconfigured firewall, debug endpoint left open, port 5432 exposed to the internet, keys never rotated. ZCP is the inverse trade-off — the **guardrails** of a managed platform. Private networking, TLS, isolated services, safe defaults, managed backups, no exposed ports unless you opt in — all built in. The agent operates through [project-scoped MCP tools](/zcp/reference/mcp-operations) instead of `sudo` on a fresh Ubuntu box, so the mistakes that matter on a VPS aren't reachable from where it sits. + +#### Cloud dev environments with AI + +_GitHub Codespaces + Copilot Workspace · Gitpod · Coder_ + +A managed Linux container in the cloud with your editor and an AI assistant attached. Solves "where the agent runs" so state survives between sessions and machine setup stops being a question. The `zcp` service is the closest thing in this category — managed Linux container, Cloud IDE, your agent of choice — except it lives *inside* a Zerops project, addressing the project's runtimes, database, and cache by hostname, with deploys and logs as primitives the agent can call. #### Bundled agent platforms _Replit Agent · Lovable · Bolt.new · v0 · Devin · OpenHands_ -The agent, the workspace, the stack, and (often) the hosting target are fused into one closed product. Fast for prototypes, decisions are made for you. Switching agents usually means switching platforms, and production-grade primitives — multi-environment promotion, private networking, real observability — tend to be limited or absent. **ZCP doesn't bundle**: the agent and model stay yours on your own subscription, and the platform underneath is normal Zerops with the full stack of managed services, networking, deploys, and observability. +The agent, the workspace, the stack, and (often) the hosting target are fused into one closed product. Fast for prototypes. Decisions are made for you. Switching agents usually means switching platforms, and production-grade primitives — a real dev/stage/prod release flow, private networking, observability — tend to be limited or absent. **ZCP doesn't bundle**: the agent and model stay yours on your own subscription, and the platform underneath is normal Zerops with the full stack of managed services, networking, deploys, and observability. + +#### Local coding agents + +_Cursor · Windsurf · Zed · Claude Code · Codex CLI · Aider · Cline_ + +The agent runs on your machine, edits your local checkout, runs local commands. Excellent at code changes. Anything beyond your machine — databases, networking, deploy targets, runtime logs — is yours to wire in. Install the ZCP MCP locally and run `zcli vpn up`, and any of these agents can now reach a real Zerops project alongside your local code: services addressed by hostname, deploys, logs, events. #### Code-execution sandboxes @@ -9574,9 +9575,9 @@ Libraries for building and shipping agents to *your* users — tool calling, mem ## Where to start - [Quickstart](/zcp/quickstart) — AI Agent recipe, Claude Code, and a real product change in about five minutes. -- [ZCP MCP overview](/zcp/overview) — What the `zcp` MCP gives the agent, where it runs, and how workflow-guided runs end in proof or blocker. +- [ZCP MCP overview](/zcp/overview) — What the `zcp` MCP gives the agent, where it runs, and how a run ends in proof or a blocker. - [Remote or local setup](/zcp/setup/choose-workspace) — Choose where the `zcp` MCP runs — inside Zerops with bundled tools, or beside your local editor. -- [Build and ship](/zcp/workflows/build-with-zcp) — Runtime layout, development, delivery, packaging, and production promotion. +- [Build and ship](/zcp/workflows/build-with-zcp) — Runtime layout, development, delivery, packaging, and production release. ---------------------------------------- @@ -38847,13 +38848,13 @@ For advanced configurations or custom requirements: # Zcp > Concept > How It Works -The ZCP MCP setup gives a coding agent an operating loop for Zerops app work. The agent reads live state, chooses the app runtime and dependencies, changes the app, deploys through Zerops, verifies real behavior, and returns proof or a blocker. +The ZCP MCP setup gives a coding agent a **work loop** for Zerops app work. The agent reads live state, chooses the app runtime and dependencies, changes the app, deploys through Zerops, verifies real behavior, and returns proof or a blocker. -The loop is carried by bounded Zerops operations, Zerops-specific knowledge, and instructions for what to inspect, what to change, when to ask, and what counts as done. You describe the product outcome; the agent uses the loop to make its decisions visible while it works. +The loop is carried by project-scoped Zerops operations, Zerops-specific knowledge, and instructions for what to inspect, what to change, when to ask, and what counts as done. You describe the product outcome; the agent uses the loop to make its decisions visible while it works. The source of truth is the real project: runtimes, managed services, env references, logs, events, and deploy results. A separate preview sandbox is not the source of truth. -## The control loop +## The work loop ```mermaid flowchart TD @@ -38938,19 +38939,19 @@ Chat history is not the source of truth. If the agent sounds confused, starts fr Read current project status and tell me where this project stands before changing anything. ``` -## What the workflow coordinates +## What the workflow handles -The generated workflow is opinionated about the concerns an agent must resolve during app work. You usually do not name them in the prompt. The tools and instructions supply the state, guidance, operations, and done criteria so the agent can work from evidence. +The workflow handles the things an agent must resolve during app work so you do not have to name them in the prompt. The tools and instructions supply the state, guidance, operations, and verification surface so the agent can work from evidence. -| Concern | What the agent gets | What that means for you | -| ---------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| What the workflow handles | What the agent gets | What that means for you | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | Live state | Services, env-var keys, Zerops references, recent events, logs, verification output, and saved work state. | You do not have to paste a service inventory or reconstruct what happened. | | Runtime target | Rules for choosing the app runtime that should receive code changes. | The agent can identify where app work belongs before editing or deploying. | | Managed services | Knowledge and env wiring patterns for database, cache, queue, search, storage, mail, and similar services. | Product intent can imply real dependencies without a manual wiring checklist. | | Service setup | Operations for using existing services or creating missing runtimes and dependencies. | Runtime layout and dependencies can be established before app work starts. | | App wiring | Zerops-specific rules for `zerops.yaml`, env references, ports, commands, public access, and build/deploy behavior. | The app is wired for Zerops rather than for a generic cloud template. | | Deploy evidence | Build logs, runtime logs, platform events, service status, HTTP checks, and structured verification output. | A failed deploy becomes a diagnosis surface instead of guesswork. | -| Behavior proof | A done gate based on the requested behavior, not only on a successful build or reachable root URL. | The final answer can point to what was actually checked. | +| Behavior proof | The proof gate is the requested behavior, not only a successful build or reachable root URL. | The final answer can point to what was actually checked. | | Delivery handoff | Direct proof first; then git push, CI, or human handoff when that is the chosen delivery choice. | Shipping setup follows a verified running result. | | Delivery handoff | Direct proof first; then git push, CI, or human handoff when that is the chosen delivery choice. | Shipping setup follows a verified running result. | @@ -38985,17 +38986,7 @@ The first functional deploy goes directly through MCP so the agent has a running ## Recovery is evidence-driven -When something fails, the agent has the evidence surface that matches the failure: - -| Failure pattern | Useful evidence | -| ------------------------ | ------------------------------------------------------------------------------------------------------------ | -| Build failed | Build logs, build commands, dependency manifests, deploy file list. | -| Runtime failed to start | Runtime or prepare logs, start command, ports, env references. | -| Route or behavior failed | Verify output, HTTP response, runtime logs at request time, stored state. | -| Network access failed | VPN, SSH, DNS, subdomain readiness, service status, named transport error. | -| Config was rejected | Field-level rejection, `zerops.yaml`, setup name, env reference, service settings. | -| Credential failed | The named credential surface: Zerops token, git token, SSH, managed-service credential, or external API key. | -| Credential failed | The named credential surface: Zerops token, git token, SSH, managed-service credential, or external API key. | +When something fails, the workflow puts the agent on the matching evidence surface — build logs for build failures, prepare/runtime logs for start failures, verify output and request-time logs for behavior failures, transport surfaces for network failures, field-level rejection for config, and the named credential surface for credential failures. The full categories, what to read first, and what to avoid live in [Troubleshooting](/zcp/reference/troubleshooting). Retrying the same deploy without new evidence is not progress. The loop pushes toward one of three outcomes: fix from a cause, ask for the missing decision, or report a blocker with the category, evidence read, and attempts made. @@ -39016,14 +39007,7 @@ The final answer should make proof inspectable: runtime name, URL or endpoint, b ## Delivery happens after proof -Delivery choice controls how future changes ship after a verified runtime exists: - -| Choice | Use when | -| ------------------ | ------------------------------------------------------------------------------ | -| Keep direct deploy | Early development, demos, dev/stage iteration, or agent-owned runtime changes. | -| Push to git | You want commits, review, repository history, or a repo-triggered build. | -| External handoff | CI, release management, or a human owns the next deploy. | -| External handoff | CI, release management, or a human owns the next deploy. | +Delivery choice controls how future changes ship after a verified runtime exists: keep direct deploy, push to git, or hand off to CI/release/human action. The user-facing version of this choice and what to tell the agent for each option lives in [Build and ship → Choose delivery after proof](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). Packaging a running service turns a deployed runtime into a re-importable bundle for another Zerops project. It is useful for handoff or reuse after proof, not for deploying the next app change; see [Package a running service](/zcp/workflows/package-running-service). @@ -39055,8 +39039,8 @@ That is the practical difference between "the agent wrote code" and "the app tas ## Next steps -- [Build and ship](/zcp/workflows/build-with-zcp) - normal app work after setup. -- [Workflows in depth](/zcp/reference/agent-workflow) - process gates, generated files, runtime layouts, delivery terms, and completion evidence. +- [Build and ship](/zcp/workflows/build-with-zcp) — normal app work after setup. +- [Workflows in depth](/zcp/reference/agent-workflow) — process gates, generated files, runtime layouts, delivery labels, and completion evidence. ---------------------------------------- @@ -39072,7 +39056,7 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee **MCP server** - the Model Context Protocol server exposed by the `zcp` binary. -**ZCP MCP tools** - the bounded Zerops operations exposed to an agent or MCP-capable client. In MCP clients, this usually appears as the `zerops` server. +**ZCP MCP tools** - the project-scoped Zerops operations exposed to an agent or MCP-capable client. In MCP clients, this usually appears as the `zerops` server. **`zcp` binary** - the executable that can run inside remote setup or on your machine in local setup. @@ -39122,7 +39106,7 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee **Runtime target** - the app runtime selected for the current change, such as `appdev`, `appstage`, `app`, or a linked local target. -**Runtime layout** - which app runtime services the workflow should use: +**Runtime layout** - which app runtime services the workflow should use: - `standard` - dev runtime plus explicit stage runtime. - `dev` - one mutable development runtime. @@ -39158,7 +39142,7 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee **Package a running service** - workflow that turns one verified runtime and its managed dependencies into a re-importable, git-backed Zerops bundle. -**Production promotion** - handoff from verified dev or stage work into a separate production Zerops project through production infrastructure setup, production deploy trigger setup, and repeatable releases. +**Production release** - the release operation that moves verified dev or stage work into a separate production Zerops project. It is set up once per project (production infrastructure) and once per runtime (production deploy trigger), then runs every release. **Production boundary** - policy that production should live in a separate Zerops project without a `zcp` service and receive promoted work through CI, release process, or human action. @@ -39168,7 +39152,7 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee **Public subdomain access** - Zerops `.zerops.app` URL access for an eligible HTTP runtime. Workers and non-HTTP services do not get useful public URLs. -**Failure category** - label that points to the first useful evidence surface: +**Failure category** - label that points to the first useful evidence surface: - `build` - build phase failed. - `start` - build passed, but runtime start or prepare failed. @@ -39200,23 +39184,28 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee # Zcp > Overview -Zerops provides the project infrastructure: runtimes, managed services, networking, env vars, deploys, logs, and public access. ZCP MCP is the project-scoped tool layer that lets a coding agent work with that infrastructure through MCP. +:::info Public preview +ZCP MCP is a public preview and is under active development. If you hit a bug, confusing behavior, or a missing workflow, please report it on [Discord](/discord); feedback from real projects is especially useful. +::: -For the broader feature concept — why coding agents need project infrastructure rather than a sandbox or generated artifact — start with [Infrastructure for Coding Agents](/features/coding-agents). +This section covers, in order: -With the generated workflow instructions enabled, an app task should end in proof or a blocker. Proof means a deployed runtime plus the URL, endpoint response, UI state, worker result, or stored data that shows the requested behavior works. A blocker means the agent read the relevant Zerops evidence and names the missing credential, decision, unsupported fit, or repeated failure. +- **How a run unfolds.** [Quickstart](/zcp/quickstart) for hands-on; [How it works](/zcp/concept/how-it-works) for the work loop. +- **Where the agent runs.** [Remote or local setup](/zcp/setup/choose-workspace), [Trust model](/zcp/security/trust-model), and [Production boundary](/zcp/security/production-policy). +- **What you decide while working.** [Build and ship](/zcp/workflows/build-with-zcp), [Package a running service](/zcp/workflows/package-running-service), [Promote to production](/zcp/workflows/promote-to-production). +- **Exact labels when they matter.** [Workflows in depth](/zcp/reference/agent-workflow), [ZCP MCP tools](/zcp/reference/mcp-operations), [Troubleshooting](/zcp/reference/troubleshooting), [Glossary](/zcp/glossary). -Your prompt can stay about the product. Name the stack, runtime layout, acceptance criteria, delivery path, external credentials, or risky approval only when those decisions matter. +For the broader feature concept — why coding agents need real project infrastructure rather than a sandbox or generated artifact — start with [Infrastructure for Coding Agents](/features/coding-agents). -:::info Public preview -ZCP MCP is a public preview and is currently under active development. If you hit a bug, confusing behavior, or a missing workflow, please report it on [Discord](/discord); feedback from real projects is especially useful. -::: +With the generated workflow instructions enabled, an app task ends in proof or a blocker. **Proof** is a deployed runtime plus the URL, endpoint response, UI state, worker result, or stored data that shows the requested behavior works. A **blocker** is the agent reading the relevant Zerops evidence and naming the missing credential, decision, unsupported fit, or repeated failure. + +Your prompt can stay about the product. Name the stack, runtime layout, acceptance criteria, delivery path, external credentials, or risky approval only when those decisions matter. ## What the agent gets **Current state.** Services, runtime layout, managed dependencies, env-var keys and references, logs, events, deploy history, verification state, and saved work state. -**Zerops operations.** Tools for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. +**Zerops operations.** Project-scoped tools for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. **Workflow.** The generated instructions combine service setup and app development: inspect state, choose the runtime target, use or create services, wire code and `zerops.yaml`, deploy, verify, and choose delivery. The Zerops work stays behind the product task instead of becoming another checklist. @@ -39249,34 +39238,9 @@ To start, add remote setup in Zerops or initialize local setup beside your edito **Workspace freedom.** The `zcp@1` service is still a normal Zerops service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and adapt the workspace. Details live in [What remote workspace gives you](/zcp/setup/hosted-workspace#make-customization-persistent). :::caution Production boundary -Use this setup for development or staging work. Production should stay in a separate Zerops project and receive promoted work through your CI or release process; see [Promote to production](/zcp/workflows/promote-to-production) for the practical flow and [Production boundary](/zcp/security/production-policy) for the policy. +Use this setup for development or staging work. Production should stay in a separate Zerops project and receive released work through your CI or release process; see [Promote to production](/zcp/workflows/promote-to-production) for the practical flow and [Production boundary](/zcp/security/production-policy) for the policy. ::: -## Start here - -First-read pages: - -| If you want to | Read | -| ----------------------------------------- | -------------------------------------------------------- | -| Try the guided hands-on path | [Quickstart](/zcp/quickstart) | -| Understand the agent operating loop | [How it works](/zcp/concept/how-it-works) | -| Compare remote and local setup | [Remote or local setup](/zcp/setup/choose-workspace) | -| Start building after setup | [Build and ship](/zcp/workflows/build-with-zcp) | -| Start building after setup | [Build and ship](/zcp/workflows/build-with-zcp) | - -Specific tasks and reference: - -| If you want to | Read | -| ---------------------------------------- | --------------------------------------------------------------------------- | -| Understand remote workspace | [What remote workspace gives you](/zcp/setup/hosted-workspace) | -| Use your local editor or CLI agent | [Run locally](/zcp/setup/local-agent-bridge) | -| Decide how finished work ships | [Build and ship](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | -| Understand token access and boundaries | [Trust model](/zcp/security/trust-model) | -| Understand the feature concept | [Infrastructure for Coding Agents](/features/coding-agents) | -| Understand workflow-guided runs | [Workflows in depth](/zcp/reference/agent-workflow) | -| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | -| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | - ## What stays outside This section is not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), networking, scaling, and service references remain canonical for platform behavior. @@ -39355,9 +39319,9 @@ If the agent cannot finish, useful output names the blocker: missing credential, ## Next steps +- [Build and ship](/zcp/workflows/build-with-zcp) — Runtime layout, development work, delivery, packaging, and production release. - [How it works](/zcp/concept/how-it-works) — Live state, runtime fit, app wiring, deploy evidence, behavior proof, and blockers. -- [Remote or local setup](/zcp/setup/choose-workspace) — Choose whether the agent workspace runs inside Zerops or beside your local files and tools. -- [Build and ship](/zcp/workflows/build-with-zcp) — Runtime layout, development work, delivery, packaging, and production promotion. +- [Trust model](/zcp/security/trust-model) — What the project-scoped token lets the agent do, and where the safety boundary sits. ---------------------------------------- @@ -39365,7 +39329,7 @@ If the agent cannot finish, useful output names the blocker: missing credential, # Zcp > Reference > Agent Workflow -Use this page when you want to understand the process behind workflow-guided agent work. Normal prompts should still describe outcomes. The workflow exists so the agent can read the project, prepare the right runtime and services, make the app change, deploy, verify, recover from evidence, and stop with proof or a concrete blocker. +Use this page when you want the process and exact labels behind a run that follows the generated workflow. Normal prompts should still describe outcomes. The workflow exists so the agent can read the project, prepare the right runtime and services, make the app change, deploy, verify, recover from evidence, and stop with proof or a concrete blocker. The useful mental split is: @@ -39375,7 +39339,7 @@ The useful mental split is: | **Develop** | Code, `zerops.yaml`, env wiring, deploy, verification, recovery, and delivery choice. | The requested behavior is proved, or the blocker is concrete. | | **Develop** | Code, `zerops.yaml`, env wiring, deploy, verification, recovery, and delivery choice. | The requested behavior is proved, or the blocker is concrete. | -Bootstrap is not a marketing or onboarding step. It is the workflow's name for "make the project layout safe to work in before changing app code." +For the loop itself, see [How it works](/zcp/concept/how-it-works). This page catalogs the exact phases, routes, layouts, labels, and gates. ## Session layers @@ -39392,7 +39356,7 @@ The `zcp` service is the control surface, not the app runtime. ## What drives the workflow -The workflow-guided experience is made from four pieces: +The generated workflow is made from four pieces: | Piece | Role | | ----- | ---- | @@ -39444,44 +39408,13 @@ Bootstrap ends when the app runtime target and managed dependencies are known. I ## Runtime layouts -Runtime layout describes which app runtime services the workflow should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. - -| Layout | Meaning | Typical names | -| ------ | ------- | ------------- | -| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage is explicit. | `appdev` + `appstage` | -| `dev` | One mutable development runtime. | `appdev` | -| `simple` | One runtime with no dev/stage split. | `app` | -| `local-stage` | Local source directory linked to one Zerops runtime for deploys. | local directory + `appstage` | -| `local-only` | Local source directory with no linked deploy target yet. | local directory | -| `local-only` | Local source directory with no linked deploy target yet. | local directory | +Runtime layout describes which app runtime services the workflow should use. Exact layout labels (`standard`, `dev`, `simple`, `local-stage`, `local-only`) and the runtime names they map to live in the [Glossary](/zcp/glossary#runtime-layout). The user-facing three (dev, dev + stage, stage / linked target) are in [Build and ship → Choose the runtime layout](/zcp/workflows/build-with-zcp#choose-the-runtime-layout). Service scaling mode (`HA`/`NON_HA`) is a separate service setting. -In `standard`, stage is explicit. Work scoped to `appdev` does not silently touch `appstage`; promotion or stage verification happens when the user asks for it. +In `standard`, stage is explicit. Work scoped to `appdev` does not silently touch `appstage`; a release or stage verification happens when the user asks for it. ## Develop -Develop is the main app-work loop. It begins after bootstrap has a runtime target and dependencies. It closes only when runtime reachability and requested behavior both pass, or when the agent has a blocker that needs a human decision. - -```mermaid -flowchart TD - scope["1. Name runtime target"] - change["2. Change code/config"] - deploy["3. Direct deploy"] - serve["4. Start or restart if needed"] - reach{"5. Runtime reachable?"} - behavior{"6. Requested behavior works?"} - done(["Proof"]) - fix["7. Categorize failure -read evidence, fix"] - blocker(["Blocker"]) - - scope --> change --> deploy --> serve --> reach - reach -- yes --> behavior - reach -- no --> fix - behavior -- yes --> done - behavior -- no --> fix - fix --> deploy - fix --> blocker -``` +Develop is the main app-work loop. It begins after bootstrap has a runtime target and dependencies. It closes only when runtime reachability and requested behavior both pass, or when the agent has a blocker that needs a human decision. The loop itself is in [How it works → The work loop](/zcp/concept/how-it-works#the-work-loop). The labeled steps are: 1. **Name runtime target.** State which runtime is in scope: `appdev`, `appstage`, `app`, or a linked local target. 2. **Change code and config.** Edit app files, `zerops.yaml`, env references, migrations, seeds, framework config, or local `.env` bridge when needed. @@ -39495,20 +39428,9 @@ Reachability and requested behavior are separate gates. A green deploy with a br ## Failure categories -Failure categories point the agent to the first useful evidence surface. +Failure categories (`build`, `start`, `verify`, `network`, `config`, `credential`, `other`) point the agent to the first useful evidence surface. The full read-first / avoid playbook is in [Troubleshooting → Evidence order](/zcp/reference/troubleshooting#evidence-order); the term definitions are in the [Glossary](/zcp/glossary#failure-category). -| Category | What it means | Read first | -| -------- | ------------- | ---------- | -| `build` | Build phase failed. | Build logs, build commands, dependency manifests, deploy file list. | -| `start` | Build passed, but runtime start or prepare failed. | Prepare/runtime logs, start command, ports, env references. | -| `verify` | Runtime exists, but reachability or behavior failed. | Failing check detail, HTTP response, request-time runtime logs, behavior evidence. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | VPN, SSH, DNS, subdomain readiness, service status, named transport error. | -| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Field-level rejection, setup name, env reference, service settings. | -| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | -| `other` | No known category matched. | Raw event/log evidence; stop if repeated without a new signal. | -| `other` | No known category matched. | Raw event/log evidence; stop if repeated without a new signal. | - -Categorization is what turns retries into evidence-driven fixes. The practical recovery flow lives in [Troubleshooting](/zcp/reference/troubleshooting). +Categorization is what turns retries into evidence-driven fixes. ## Delivery after proof @@ -39523,7 +39445,7 @@ Delivery mode applies after a verified deploy. It does not replace the first pro Git-push capability, delivery mode, and build integration are separate. A project can have git-push configured while still using direct deploy for a given session. -Packaging and production promotion are deliberate handoffs after proof. Packaging turns a verified runtime into a git-backed import bundle. Production promotion moves verified work into a separate production project through GUI setup, git/CI triggers, or the team's release process. +Packaging and production release are deliberate handoffs after proof. Packaging turns a verified runtime into a git-backed import bundle. A production release moves verified work into a separate production project through GUI setup, git/CI triggers, or the team's release process. ## Generated files and state @@ -39564,16 +39486,11 @@ A clear blocker is acceptable completion only when it names the runtime in scope ## Confirmation gates -Some operations pause because the loss is not safely reversible from inside the conversation: - -- **Service deletion** requires explicit user approval in the current conversation, by service name. -- **Destructive import override** first refuses and names what would be replaced; a second call must acknowledge the same targets. - -See [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) for the user-facing confirmation flow. +Two operations pause because the loss is not safely reversible from inside the conversation: **service deletion** (named approval) and **destructive import override** (refuse-then-acknowledge). See [Tokens and credentials → What ZCP enforces for destructive actions](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) for the user-facing confirmation flow. ## Auditing a workflow -A workflow-guided run is well-shaped if the evidence answers: +A workflow run is well-shaped if the evidence answers: | Question | Evidence | | -------- | -------- | @@ -39582,14 +39499,14 @@ A workflow-guided run is well-shaped if the evidence answers: | Which runtime was developed and deployed? | Runtime target, deploy result, events, and logs. | | What proved reachability? | Verify output, service status, public URL, or HTTP probe. | | What proved behavior? | Endpoint, UI flow, job result, database/object state, or other requested proof. | -| What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production handoff note. | -| What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production handoff note. | +| What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production release handoff note. | +| What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production release handoff note. | ## Related reference -- [ZCP MCP tools](/zcp/reference/mcp-operations) - operation names and direct tool calls. -- [Troubleshooting](/zcp/reference/troubleshooting) - recovery order when a run gets stuck. -- [Glossary](/zcp/glossary) - exact terms used across the ZCP MCP reference. +- [ZCP MCP tools](/zcp/reference/mcp-operations) — operation names and direct tool calls. +- [Troubleshooting](/zcp/reference/troubleshooting) — recovery order when a run gets stuck. +- [Glossary](/zcp/glossary) — exact terms used across the ZCP MCP reference. ---------------------------------------- @@ -39597,15 +39514,48 @@ A workflow-guided run is well-shaped if the evidence answers: # Zcp > Reference > Index -Use reference pages when exact labels matter. Day-to-day app work starts in [Build and ship](/zcp/workflows/build-with-zcp). +Reference pages exist for the moments when an exact label, tool name, or recovery step matters. For day-to-day app work, start in [Build and ship](/zcp/workflows/build-with-zcp); for the loop itself, [How it works](/zcp/concept/how-it-works). + +- [Workflows in depth](/zcp/reference/agent-workflow) — Catalog of phases, routes, layouts, and labels behind a generated-workflow run. +- [ZCP MCP tools](/zcp/reference/mcp-operations) — Exact tool names and surface for custom MCP clients or agent policy. +- [Troubleshooting](/zcp/reference/troubleshooting) — When the agent is stuck, a session was interrupted, deploys keep failing, or you are taking over manually. +- [Glossary](/zcp/glossary) — Definitions for terms across pages and workflow status (`appdev`, `local-stage`, `delivery mode`, etc.). + +## Common scenarios + +
+ +
+ +SCENARIO + +### Debugging a stuck deploy + +[Troubleshooting → Evidence order](/zcp/reference/troubleshooting#evidence-order) names what to read first; [Workflows in depth → Failure categories](/zcp/reference/agent-workflow#failure-categories) maps the labels. + +
+ +
+ +SCENARIO -| Need | Page | -|---|---| -| Understand what sits behind workflow-guided agent runs: generated instructions, state, process gates, and completion evidence | [Workflows in depth](/zcp/reference/agent-workflow) | -| Use ZCP MCP tools directly or look up operation names for a custom MCP integration | [ZCP MCP tools](/zcp/reference/mcp-operations) | -| Recover a stuck or confused session | [Troubleshooting](/zcp/reference/troubleshooting) | -| Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | -| Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | +### Building a custom MCP client + +[ZCP MCP tools](/zcp/reference/mcp-operations) for the tool surface; [Workflows in depth → What drives the workflow](/zcp/reference/agent-workflow#what-drives-the-workflow) for what the generated workflow adds on top of bare tools. + +
+ +
+ +SCENARIO + +### Auditing a finished run + +[Workflows in depth → Completion evidence](/zcp/reference/agent-workflow#completion-evidence) for what should be in the final answer; [Trust model → Audit evidence](/zcp/security/trust-model#audit-evidence) for what Zerops records platform-side. + +
+ +
---------------------------------------- @@ -39621,7 +39571,7 @@ ZCP MCP is the MCP server exposed by the `zcp` binary. Any MCP-capable client ca The MCP server exposes operations. It does not, by itself, decide the whole app lifecycle. -Workflow-guided Claude Code uses generated instructions to decide sequencing: inspect state before changing things, choose the runtime target, use existing services or create missing ones, deploy, read failure evidence, verify requested behavior, and stop with proof or a blocker. +Claude Code with the generated workflow uses its instructions to decide sequencing: inspect state before changing things, choose the runtime target, use existing services or create missing ones, deploy, read failure evidence, verify requested behavior, and stop with proof or a blocker. Tool-only use is valid when you want a custom MCP client, script, dashboard, policy-gated agent, or narrow operational task. In that setup, your integration owns the sequencing: @@ -39713,7 +39663,7 @@ See [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-en ## Related reference -- [Workflows in depth](/zcp/reference/agent-workflow) - how workflow-guided runs use these tools. +- [Workflows in depth](/zcp/reference/agent-workflow) - how a generated-workflow run uses these tools. - [Troubleshooting](/zcp/reference/troubleshooting) - evidence order when a run gets stuck. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, and confirmation gates. @@ -39826,7 +39776,7 @@ Use the MCP setup in development or staging. Keep production in a separate Zerop Zerops does not prevent you from adding `zcp` to production. The policy exists because ZCP MCP gives a coding agent operational access. In production, that is the wrong blast radius for normal app development. -The production boundary does not make ZCP output disposable. The intended path is production-shaped dev or stage infrastructure, verified behavior, and then a controlled production promotion outside the agent loop. +The production boundary does not make ZCP output disposable. The intended path is production-shaped dev or stage infrastructure, verified behavior, and then a controlled production release outside the agent loop. ## Recommended project layout @@ -39849,7 +39799,7 @@ This separation matters even when the same source repository deploys to both pro ## What stage proves -Stage is the production-like rehearsal inside development or staging. It is where the agent proves the change before promotion leaves the ZCP loop. +Stage is the production-like rehearsal inside development or staging. It is where the agent proves the change before the release leaves the ZCP loop. Use stage to match production where it matters: @@ -39861,9 +39811,9 @@ Use stage to match production where it matters: Stage is not production. It should use non-production data and non-production secrets. A green stage means the change is ready for release, not that the agent should deploy to production itself. -## Promotion +## The release -After stage verifies, ZCP's job is done for that change. Promotion happens outside the agent loop: +After stage verifies, ZCP's job is done for that change. The production release happens outside the agent loop: - CI deploys a merged commit or release tag to the production project. - A release pipeline runs `zcli push` with a production-scoped `ZEROPS_TOKEN`. @@ -39898,7 +39848,7 @@ If a production deploy fails, investigate in the production project with product ## Acceptable agent involvement -The agent can still help before production promotion: +The agent can still help before the production release: - make the code change in development, - deploy and verify dev/stage runtimes, @@ -39910,7 +39860,7 @@ ZCP's involvement stops at the handoff. Production execution belongs to the rele ## Related security -- [Promote to production](/zcp/workflows/promote-to-production) - practical production promotion paths after ZCP proof. +- [Promote to production](/zcp/workflows/promote-to-production) - practical production release paths after ZCP proof. - [Trust model](/zcp/security/trust-model) - the boundary that makes this policy enforceable. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - production and development credentials stay separate. @@ -40134,9 +40084,7 @@ There are three separate credential surfaces people often mix together: | Git or CI credentials | You / your repository system | Lets finished work push to git or deploy through CI. | | Git or CI credentials | You / your repository system | Lets finished work push to git or deploy through CI. | -Remote setup injects `ZCP_API_KEY` into the `zcp` service. Local setup reads it from the `.mcp.json` env block. In both paths, the agent account is still authenticated through the agent's own login flow. - -Details: [Tokens and credentials](/zcp/security/tokens-and-project-access). +In both setups, the agent account is still authenticated through the agent's own login flow. For where each credential lives (remote injection vs `.mcp.json`), rotation, rejected token shapes, and `GIT_TOKEN` / `ZEROPS_TOKEN`, see [Tokens and credentials](/zcp/security/tokens-and-project-access). ## What the token lets the agent do @@ -40171,15 +40119,7 @@ This is why development and staging projects are the right place for ZCP. Produc Most project operations do not get an extra ZCP-specific confirmation prompt. Deploys, env changes, restarts, scaling, and public-access changes are normal project operations and are audited through platform evidence. -ZCP MCP adds hard gates where the loss is not safely reversible from the conversation: - -| Operation | Gate | -| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -| Service deletion | Requires explicit same-conversation approval that names the service. Remote setup also blocks deleting the `zcp` service it is running in. | -| Wholesale service replacement after failed deploy history | Requires the agent to read failure evidence first, summarize what would be replaced, and ask for explicit confirmation. | -| Wholesale service replacement after failed deploy history | Requires the agent to read failure evidence first, summarize what would be replaced, and ask for explicit confirmation. | - -Approval from an old chat does not carry forward. A new conversation needs a new approval. +ZCP MCP adds hard gates where the loss is not safely reversible from the conversation: **service deletion** (explicit same-conversation approval by service name; remote setup also blocks deleting the `zcp` service it is running in) and **wholesale service replacement after failed deploy history** (refuse-then-acknowledge with failure evidence first). Approval from an old chat does not carry forward. The full enforcement rules are in [Tokens and credentials → What ZCP enforces for destructive actions](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions). ## Audit evidence @@ -40304,7 +40244,7 @@ Taking over is straightforward in this setup. You open the same workspace, termi ## What it includes -- **`zcp@1` workspace service.** Runs ZCP inside Zerops and gives the agent bounded operations. +- **`zcp@1` workspace service.** Runs ZCP inside Zerops and gives the agent project-scoped operations. - **Platform-injected `ZCP_API_KEY`.** Zerops injects the token into the workspace; you normally do not set it by hand. - **Bundled Claude Code when enabled.** The **Include Coding Agent** option installs and preconfigures Claude Code. You authenticate with your own Claude subscription login or API key. - **Browser VS Code when enabled.** The **Cloud IDE** option gives you a browser editor, terminal, and a place to supervise or take over the agent session. @@ -40384,8 +40324,8 @@ Tools installed inside the `zcp` service may see workspace environment variables | `ZCP_API_KEY` | Injected by Zerops into the `zcp` workspace. | | Agent account | Your Claude subscription login or model API credential. Zerops wires the agent to ZCP, but the agent account remains yours. | | Git credentials | Configured inside the workspace when the agent should commit or push from remote setup. | -| Production promotion | A separate production project and release process. | -| Production promotion | A separate production project and release process. | +| Production release | A separate production project and release process. | +| Production release | A separate production project and release process. | ## Runtime file access @@ -40556,7 +40496,7 @@ Keep `.env` out of git. It contains real connection values. ## 8. Link a deploy target -Local setup needs a Zerops runtime when the agent should deploy from your working directory. A stage runtime is the usual target because it lets local work prove itself before production promotion. +Local setup needs a Zerops runtime when the agent should deploy from your working directory. A stage runtime is the usual target because it lets local work prove itself before the production release. If there is exactly one runtime, the agent can use it automatically. If multiple runtimes exist, it should ask which one to link: @@ -40588,18 +40528,6 @@ The MCP server does not own your git credentials. In local setup, your local git - [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and destructive confirmations. ----------------------------------------- - -# Zcp > Workflows > Build And Verify App - - -This page moved to [Build and ship](/zcp/workflows/build-with-zcp). - -- Use [Development](/zcp/workflows/build-with-zcp#develop-with-live-project-context) for app work, deploy, and verification. -- Use [How it works -> Verification has two layers](/zcp/concept/how-it-works#verification-has-two-layers) for the deeper verification model. -- Use [Troubleshooting](/zcp/reference/troubleshooting) for symptoms and recovery moves. - - ---------------------------------------- # Zcp > Workflows > Build With Zcp @@ -40612,7 +40540,7 @@ Build a task board. Tasks should stay saved after refresh. ``` -A prompt can be that short when the outcome is enough. Add detail when it changes behavior, architecture, stack, runtime layout, acceptance criteria, credentials, delivery, packaging, or production promotion. +A prompt can be that short when the outcome is enough. Add detail when it changes behavior, architecture, stack, runtime layout, acceptance criteria, credentials, delivery, packaging, or the production release. The expected output is a verified running change, not only generated files. The agent should prove the request against a real runtime, real managed services when used, and the logs, events, and checks that explain what happened. @@ -40644,7 +40572,7 @@ The expected output is a verified running change, not only generated files. The
  • Direct deploy, git, or CI
  • Optional package bundle
  • -
  • Production promotion
  • +
  • Production release
@@ -40661,7 +40589,7 @@ The main user-facing choice is runtime layout. Let the agent infer it from the p | Runtime layout | Use when | What to tell the agent | | ------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------- | | **Dev** | You want one mutable runtime for fast iteration, experiments, or early app work. | `Build a small Node.js API on dev.` | -| **Dev + stage** | You want a development runtime plus a separate runtime for review or promotion rehearsal. | `Build a Node.js API with PostgreSQL on dev+stage.` | +| **Dev + stage** | You want a development runtime plus a separate runtime for review or release rehearsal. | `Build a Node.js API with PostgreSQL on dev+stage.` | | **Stage / linked target** | You work from local files or a single target runtime and want the workflow to use that target. | `Use appstage as the deploy target for this local app.` | | **Stage / linked target** | You work from local files or a single target runtime and want the workflow to use that target. | `Use appstage as the deploy target for this local app.` | @@ -40715,9 +40643,9 @@ The agent may still ask which runtime to package, which half of a dev+stage pair Packaging is not the next deploy of the same app. For the next app change, keep using the normal build/deploy/verify loop. Use packaging when the output you want is a git-backed import bundle; see [Package a running service](/zcp/workflows/package-running-service). -## 5. Prepare production promotion {#prepare-production-promotion} +## 5. Prepare the production release {#prepare-production-release} -Production promotion is where authority changes. The agent prepares verified work and release evidence; production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. +The production release is where authority changes. The agent prepares verified work and release evidence; production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. The useful split is: @@ -40738,33 +40666,11 @@ For a completed app task, the agent should report: - the deploy or verification target, - the URL, endpoint, UI state, job result, or stored data that proves the requested behavior, - managed services, env vars, or delivery settings it touched, -- the delivery preference, packaging output, or production promotion state that now applies. +- the delivery preference, packaging output, or production release state that now applies. If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the decision or credential needed from you. ----------------------------------------- - -# Zcp > Workflows > Create Or Adopt Services - - -This page moved to [Build and ship](/zcp/workflows/build-with-zcp). - -- Use [Runtime layout](/zcp/workflows/build-with-zcp#choose-the-runtime-layout) for existing services, missing services, and the handoff from setup into app work. -- Use [Workflows in depth](/zcp/reference/agent-workflow) for workflow process details and exact labels. - - ----------------------------------------- - -# Zcp > Workflows > Delivery Handoff - - -This page moved to [Build and ship](/zcp/workflows/build-with-zcp). - -- Use [Delivery after proof](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) for the user-facing choices: keep direct deploy, push verified work to git, or hand off to CI/release tooling. -- Use [Workflows in depth](/zcp/reference/agent-workflow#delivery-after-proof) for delivery labels and workflow context. - - ---------------------------------------- # Zcp > Workflows > Package Running Service @@ -40795,7 +40701,7 @@ Package a runtime when you need: Do not use packaging for: - the next app deploy; use the normal build, deploy, and verify loop, -- production promotion; production needs its own infrastructure, credentials, domains, and release trigger, +- the production release; production needs its own infrastructure, credentials, domains, and release trigger, - moving managed-service data; backups and restores are separate. For production setup, follow [Promote to production](/zcp/workflows/promote-to-production). Packaging can help create a reusable app bundle, but it does not decide production infrastructure or release policy. @@ -40855,7 +40761,7 @@ Before opening the new project to users: # Zcp > Workflows > Promote To Production -Use this after the agent has proved work in development or stage. Production promotion is the point where authority changes: the agent can prepare proof, source changes, and setup notes, but production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. +Use this after the agent has proved work in development or stage. The production release is the point where authority changes: the agent can prepare proof, source changes, and setup notes, but production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. Production should be a separate Zerops project without a `zcp` service. That keeps the development agent out of the production blast radius while still letting verified work reach production. diff --git a/apps/docs/static/llms-small.txt b/apps/docs/static/llms-small.txt index 599c6c4bd..814c469a2 100644 --- a/apps/docs/static/llms-small.txt +++ b/apps/docs/static/llms-small.txt @@ -9012,18 +9012,18 @@ Remember that only publicly accessible objects will be cached by the CDN. Privat # Features > Coding Agents -Zerops was built on the idea of **environment parity** — to give developers the whole development lifecycle, from remote development to highly available production, with all the observability and developer tools for maximum flexibility, while keeping the configs reasonable. And it turns out that this is **exactly what coding agents need** to produce and iterate on production-ready applications. +Zerops was built on the idea of **environment parity** — giving developers the full development lifecycle, from remote development to highly available production, with the observability and developer tools for maximum flexibility, and sensible defaults so the configs stay reasonable. Turns out that's **exactly what coding agents need** to produce and iterate on production-ready applications. ## What is it -An **[MCP server](/zcp/overview)** for your agent that can run locally or on a remote container that makes use of the flexibility and processes Zerops allows. There is no "Zerops system prompt" hogging your context window. The MCP server is a thin layer that makes **your agent a Zerops platform power user** — when needed — so it understands: +An **[MCP server](/zcp/overview)** for your agents — with an optional remote cloud development environment container to run them in — that makes use of the flexibility and processes Zerops provides. There's no "Zerops system prompt" hogging your context window. The MCP server is a thin layer that makes **your agent a Zerops platform power user** — when needed — so it understands: - how networking, scaling, debugging, build & deployment, environment variables, and service provisioning work on the platform - the development loop: start a dev server → implement → deploy to stage → verify -It's the depth and flexibility of the platform that then give the agent its superpowers. A single agent can scaffold and iterate on projects with multiple runtimes (`app`, `api`, `worker`) backed by multiple [managed services](/features/infrastructure) (object storage `storage`, Postgres `db`, Elasticsearch `search`, NATS `broker`, Valkey `cache`) — end-to-end coverage from development to highly available production — just as easily as it can work on a simple Next.js presentational website. +The depth of the platform underneath is what gives the agent room to actually work. A single agent can scaffold and iterate on projects with multiple runtimes (`app`, `api`, `worker`) backed by multiple [managed services](/features/infrastructure) (object storage `storage`, Postgres `db`, Elasticsearch `search`, NATS `broker`, Valkey `cache`) — just as easily as it can work on a simple Next.js presentational website. -A **[recipe ecosystem](https://app.zerops.io/recipes)** covering the most popular frameworks — pre-prepared for the whole lifecycle — gives the agent a baseline to start from, or a guide for setting up any given stack end-to-end on Zerops. +A **[recipe ecosystem](https://app.zerops.io/recipes)** covering the most popular frameworks — pre-prepared for the whole lifecycle — gives the agent a baseline to start from, or a guide for setting up any stack on Zerops. ```text ┌─ ZCP MCP ────┐ ┌─ recipes for any stack ────────────────────────────────┐ @@ -9048,6 +9048,7 @@ A **[recipe ecosystem](https://app.zerops.io/recipes)** covering the most popula │ ▼ │ │ ▼ │ │ ┌────────────┐ │ │ ┌──────────┐ │ │ │ zcp │ │ │ │ zcp │ │ +│ │ Ubuntu │ │ │ │ Ubuntu │ │ │ └────────────┘ │ │ └──────────┘ │ │ │ │ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ ┌──────────┐ │ @@ -9076,21 +9077,21 @@ Most coding-agent platforms went agent-first, then bolted on infrastructure — ## Main features -### Bring your own agent and your own subscription +### Your agent, your subscription -Currently Claude Code, with Codex, Gemini CLI, opencode, and any other MCP-capable agent coming soon. Modify the full Linux Ubuntu container the agent runs on; configure the agent your way. **Zerops doesn't resell tokens** — sign in with your own subscription. We just provide the infrastructure to run the coding agents and what they build. +Bring whichever agent you already use. Claude Code today; Codex, Gemini CLI, opencode, and any MCP-capable client next. The container is a regular Ubuntu — install your own CLI tools, drop in your `.claude` / `.cursor` configs, attach your IDE over SSH. **Zerops doesn't resell tokens or proxy your model calls** — sign in with your own Anthropic / OpenAI / Google subscription and the agent talks to its provider directly. -### Run locally or remote, access however you want +### An ordinary Zerops project underneath -The **[`zcp` service](/zcp/setup/hosted-workspace)** — a `zcp@1` Linux container the agent can live inside, on the project's private network, with a bundled coding agent CLI and Browser VS Code as options. The **[`zcp` MCP](/zcp/setup/local-agent-bridge)** — an MCP server that exposes one project's Zerops operations through bounded, tagged tools that any MCP-capable agent client can call. Use one, the other, or both. The project surface stays the same. +The agent operates inside a normal Zerops project — same shape as production. Managed databases (PostgreSQL, MariaDB, ClickHouse), key-value stores (KeyDB, Valkey), search (Elasticsearch, Meilisearch, Typesense), vector store (Qdrant), message queues (NATS, Kafka), object and shared storage, managed Nginx — all on a private network, addressable by hostname. The same pipeline that deploys here can deploy to a separate HA production project with no `zcp` service attached. Not a sandbox the work outgrows. -### Seamless handover between human and AI +### Human ↔ agent handover -The agent runs on the same cloud development environment a human would use, and it operates on it just as a human developer would. The human can jump in at any time through any access — Cloud IDE, your local IDE over SSH, or your terminal. +The agent and the human share the same workspace — same filesystem, same SSH key, same Cloud IDE in the browser, same remote-dev attach from your local IDE. Take over mid-session, set something up before the agent runs, debug in the same container the agent just left. Nothing reaches production until you merge the PR — the agent opens it, you gate it. -### Use any technological stack you want +### Recipes for any stack -Zerops provides a curated set of recipes for most popular frameworks and programming languages. Pick one, select the **AI Agent** environment, and within minutes you have a fully working setup: Cloud IDE, authorized coding agent, and Zerops knowledge so the agent knows exactly how to develop, debug, and deploy on it. +Runtimes for Bun, Deno, Node.js, Go, Python, Rust, Java, .NET, PHP, Elixir, Gleam — with curated recipes for the framework on top: Next.js, Nuxt, Astro, Svelte, React, Vue, Angular, Solid, Qwik, Analog, NestJS, Laravel, and more. Pick a recipe, select the **AI Agent** environment, and the project comes up with a working dev runtime, a stage runtime, the right managed services wired in, and a coding agent briefed on the Zerops surface. ## How it works in practice @@ -9193,7 +9194,7 @@ The agent runs through two workflows. **Bootstrap** settles which services and r zcp (local or remote) │ ▼ - edit code on appdev ◀───────────────────────┐ + edit code on appdev ◀────────────────────────┐ └ dev server, hot reload │ │ │ ▼ │ @@ -9210,7 +9211,7 @@ The agent runs through two workflows. **Bootstrap** settles which services and r URL (proof) fix from evidence ─────┘ ``` -Once the loop produces verified work, the agent doesn't push to production — it opens a PR from the dev project. **A human review gates the merge**, then a release tag or merge triggers automatic CI/CD into a [separate production project](/zcp/security/production-policy) that has no `zcp` service at all. +Once the loop produces verified work, the agent doesn't push to production. It hands off — through whichever path your team uses: a tag push, a protected-branch merge, a manual `zcli` release, a pull request, or a CI job. **A human gates the release**, and the production deploy runs into a [separate production project](/zcp/security/production-policy) that has no `zcp` service at all. ```text Dev project GitHub Prod project @@ -9230,18 +9231,6 @@ Once the loop produces verified work, the agent doesn't push to production — i With the model clear, here's where ZCP sits among adjacent tools. People say "agent infrastructure," "infra for Claude agents," "agent hosting," or "agent sandboxes" to mean different things — usually one of the categories below. -#### Local coding agents - -_Cursor · Windsurf · Zed · Claude Code · Codex CLI · Aider · Cline_ - -The agent runs on your machine, edits your local checkout, runs local commands. Excellent at code changes. Anything beyond your machine — databases, networking, deploy targets, runtime logs — is yours to wire in. Install the ZCP MCP locally and run `zcli vpn up`, and any of these agents now reaches a real Zerops project alongside your local code: services addressed by hostname, deploys, logs, events. - -#### Cloud dev environments with AI - -_GitHub Codespaces + Copilot Workspace · Gitpod · Coder_ - -A managed Linux container in the cloud with your editor and an AI assistant attached. Solves "where the agent runs" so state survives between sessions and machine setup stops being a question. The `zcp` service is the closest thing in this category — managed Linux container, Cloud IDE, your agent of choice — except it lives *inside* a Zerops project, addressing the project's runtimes, database, and cache by hostname, with deploys and logs as primitives the agent can call. - #### PaaS MCPs _AWS MCP servers · Railway MCP · Fly.io MCP · Render MCP_ @@ -9252,13 +9241,25 @@ Most major PaaS providers have shipped an MCP — provision services, deploy cod _Hetzner Cloud MCP · DigitalOcean MCP · Linode MCP · raw SSH/server MCPs_ -"Let your agent manage your Hetzner VPS." An MCP gives the agent access to a Linux box — provision it, SSH in, install Docker and Postgres and nginx, set up systemd units, point a domain at it. Maximum control, lowest cost, full Linux to play with. The same reason senior developers reach for managed platforms applies here: even if you *can* harden SSH, write nginx configs, tune `pg_hba.conf`, rotate TLS, set firewall rules, run fail2ban, and stay on top of CVEs across kernel, OpenSSL, Docker, Postgres, and nginx — you usually don't want to. Each layer is its own domain with its own gotchas, each is a security surface that drifts the moment you stop tending it, and every project becomes a snowflake. Hand that work to an agent and you also hand it the blast radius: misconfigured firewall, debug endpoint left open, port 5432 exposed to the internet, keys never rotated. ZCP is the inverse trade-off — the **guardrails** of a managed platform. Private networking, TLS, isolated services, safe defaults, managed backups, no exposed ports unless you opt in — all built in. The agent operates through [bounded MCP tools](/zcp/reference/mcp-operations) instead of `sudo` on a fresh Ubuntu box, so the mistakes that matter on a VPS aren't reachable from where it sits. +"Let your agent manage your Hetzner VPS." An MCP gives the agent access to a Linux box — provision it, SSH in, install Docker and Postgres and nginx, set up systemd units, point a domain at it. Maximum control, lowest cost, full Linux to play with. The same reason senior developers reach for managed platforms applies here: even if you *can* harden SSH, write nginx configs, tune `pg_hba.conf`, rotate TLS, set firewall rules, run fail2ban, and stay on top of CVEs across kernel, OpenSSL, Docker, Postgres, and nginx — you usually don't want to. Each layer is its own domain with its own gotchas, each is a security surface that drifts the moment you stop tending it, and every project becomes a snowflake. Hand that work to an agent and you also hand it the blast radius: misconfigured firewall, debug endpoint left open, port 5432 exposed to the internet, keys never rotated. ZCP is the inverse trade-off — the **guardrails** of a managed platform. Private networking, TLS, isolated services, safe defaults, managed backups, no exposed ports unless you opt in — all built in. The agent operates through [project-scoped MCP tools](/zcp/reference/mcp-operations) instead of `sudo` on a fresh Ubuntu box, so the mistakes that matter on a VPS aren't reachable from where it sits. + +#### Cloud dev environments with AI + +_GitHub Codespaces + Copilot Workspace · Gitpod · Coder_ + +A managed Linux container in the cloud with your editor and an AI assistant attached. Solves "where the agent runs" so state survives between sessions and machine setup stops being a question. The `zcp` service is the closest thing in this category — managed Linux container, Cloud IDE, your agent of choice — except it lives *inside* a Zerops project, addressing the project's runtimes, database, and cache by hostname, with deploys and logs as primitives the agent can call. #### Bundled agent platforms _Replit Agent · Lovable · Bolt.new · v0 · Devin · OpenHands_ -The agent, the workspace, the stack, and (often) the hosting target are fused into one closed product. Fast for prototypes, decisions are made for you. Switching agents usually means switching platforms, and production-grade primitives — multi-environment promotion, private networking, real observability — tend to be limited or absent. **ZCP doesn't bundle**: the agent and model stay yours on your own subscription, and the platform underneath is normal Zerops with the full stack of managed services, networking, deploys, and observability. +The agent, the workspace, the stack, and (often) the hosting target are fused into one closed product. Fast for prototypes. Decisions are made for you. Switching agents usually means switching platforms, and production-grade primitives — a real dev/stage/prod release flow, private networking, observability — tend to be limited or absent. **ZCP doesn't bundle**: the agent and model stay yours on your own subscription, and the platform underneath is normal Zerops with the full stack of managed services, networking, deploys, and observability. + +#### Local coding agents + +_Cursor · Windsurf · Zed · Claude Code · Codex CLI · Aider · Cline_ + +The agent runs on your machine, edits your local checkout, runs local commands. Excellent at code changes. Anything beyond your machine — databases, networking, deploy targets, runtime logs — is yours to wire in. Install the ZCP MCP locally and run `zcli vpn up`, and any of these agents can now reach a real Zerops project alongside your local code: services addressed by hostname, deploys, logs, events. #### Code-execution sandboxes @@ -9275,9 +9276,9 @@ Libraries for building and shipping agents to *your* users — tool calling, mem ## Where to start - [Quickstart](/zcp/quickstart) — AI Agent recipe, Claude Code, and a real product change in about five minutes. -- [ZCP MCP overview](/zcp/overview) — What the `zcp` MCP gives the agent, where it runs, and how workflow-guided runs end in proof or blocker. +- [ZCP MCP overview](/zcp/overview) — What the `zcp` MCP gives the agent, where it runs, and how a run ends in proof or a blocker. - [Remote or local setup](/zcp/setup/choose-workspace) — Choose where the `zcp` MCP runs — inside Zerops with bundled tools, or beside your local editor. -- [Build and ship](/zcp/workflows/build-with-zcp) — Runtime layout, development, delivery, packaging, and production promotion. +- [Build and ship](/zcp/workflows/build-with-zcp) — Runtime layout, development, delivery, packaging, and production release. ---------------------------------------- @@ -32170,13 +32171,13 @@ For advanced configurations or custom requirements: # Zcp > Concept > How It Works -The ZCP MCP setup gives a coding agent an operating loop for Zerops app work. The agent reads live state, chooses the app runtime and dependencies, changes the app, deploys through Zerops, verifies real behavior, and returns proof or a blocker. +The ZCP MCP setup gives a coding agent a **work loop** for Zerops app work. The agent reads live state, chooses the app runtime and dependencies, changes the app, deploys through Zerops, verifies real behavior, and returns proof or a blocker. -The loop is carried by bounded Zerops operations, Zerops-specific knowledge, and instructions for what to inspect, what to change, when to ask, and what counts as done. You describe the product outcome; the agent uses the loop to make its decisions visible while it works. +The loop is carried by project-scoped Zerops operations, Zerops-specific knowledge, and instructions for what to inspect, what to change, when to ask, and what counts as done. You describe the product outcome; the agent uses the loop to make its decisions visible while it works. The source of truth is the real project: runtimes, managed services, env references, logs, events, and deploy results. A separate preview sandbox is not the source of truth. -## The control loop +## The work loop ```mermaid flowchart TD @@ -32261,19 +32262,19 @@ Chat history is not the source of truth. If the agent sounds confused, starts fr Read current project status and tell me where this project stands before changing anything. ``` -## What the workflow coordinates +## What the workflow handles -The generated workflow is opinionated about the concerns an agent must resolve during app work. You usually do not name them in the prompt. The tools and instructions supply the state, guidance, operations, and done criteria so the agent can work from evidence. +The workflow handles the things an agent must resolve during app work so you do not have to name them in the prompt. The tools and instructions supply the state, guidance, operations, and verification surface so the agent can work from evidence. -| Concern | What the agent gets | What that means for you | -| ---------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| What the workflow handles | What the agent gets | What that means for you | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | Live state | Services, env-var keys, Zerops references, recent events, logs, verification output, and saved work state. | You do not have to paste a service inventory or reconstruct what happened. | | Runtime target | Rules for choosing the app runtime that should receive code changes. | The agent can identify where app work belongs before editing or deploying. | | Managed services | Knowledge and env wiring patterns for database, cache, queue, search, storage, mail, and similar services. | Product intent can imply real dependencies without a manual wiring checklist. | | Service setup | Operations for using existing services or creating missing runtimes and dependencies. | Runtime layout and dependencies can be established before app work starts. | | App wiring | Zerops-specific rules for `zerops.yaml`, env references, ports, commands, public access, and build/deploy behavior. | The app is wired for Zerops rather than for a generic cloud template. | | Deploy evidence | Build logs, runtime logs, platform events, service status, HTTP checks, and structured verification output. | A failed deploy becomes a diagnosis surface instead of guesswork. | -| Behavior proof | A done gate based on the requested behavior, not only on a successful build or reachable root URL. | The final answer can point to what was actually checked. | +| Behavior proof | The proof gate is the requested behavior, not only a successful build or reachable root URL. | The final answer can point to what was actually checked. | | Delivery handoff | Direct proof first; then git push, CI, or human handoff when that is the chosen delivery choice. | Shipping setup follows a verified running result. | | Delivery handoff | Direct proof first; then git push, CI, or human handoff when that is the chosen delivery choice. | Shipping setup follows a verified running result. | @@ -32308,17 +32309,7 @@ The first functional deploy goes directly through MCP so the agent has a running ## Recovery is evidence-driven -When something fails, the agent has the evidence surface that matches the failure: - -| Failure pattern | Useful evidence | -| ------------------------ | ------------------------------------------------------------------------------------------------------------ | -| Build failed | Build logs, build commands, dependency manifests, deploy file list. | -| Runtime failed to start | Runtime or prepare logs, start command, ports, env references. | -| Route or behavior failed | Verify output, HTTP response, runtime logs at request time, stored state. | -| Network access failed | VPN, SSH, DNS, subdomain readiness, service status, named transport error. | -| Config was rejected | Field-level rejection, `zerops.yaml`, setup name, env reference, service settings. | -| Credential failed | The named credential surface: Zerops token, git token, SSH, managed-service credential, or external API key. | -| Credential failed | The named credential surface: Zerops token, git token, SSH, managed-service credential, or external API key. | +When something fails, the workflow puts the agent on the matching evidence surface — build logs for build failures, prepare/runtime logs for start failures, verify output and request-time logs for behavior failures, transport surfaces for network failures, field-level rejection for config, and the named credential surface for credential failures. The full categories, what to read first, and what to avoid live in [Troubleshooting](/zcp/reference/troubleshooting). Retrying the same deploy without new evidence is not progress. The loop pushes toward one of three outcomes: fix from a cause, ask for the missing decision, or report a blocker with the category, evidence read, and attempts made. @@ -32339,14 +32330,7 @@ The final answer should make proof inspectable: runtime name, URL or endpoint, b ## Delivery happens after proof -Delivery choice controls how future changes ship after a verified runtime exists: - -| Choice | Use when | -| ------------------ | ------------------------------------------------------------------------------ | -| Keep direct deploy | Early development, demos, dev/stage iteration, or agent-owned runtime changes. | -| Push to git | You want commits, review, repository history, or a repo-triggered build. | -| External handoff | CI, release management, or a human owns the next deploy. | -| External handoff | CI, release management, or a human owns the next deploy. | +Delivery choice controls how future changes ship after a verified runtime exists: keep direct deploy, push to git, or hand off to CI/release/human action. The user-facing version of this choice and what to tell the agent for each option lives in [Build and ship → Choose delivery after proof](/zcp/workflows/build-with-zcp#choose-delivery-after-proof). Packaging a running service turns a deployed runtime into a re-importable bundle for another Zerops project. It is useful for handoff or reuse after proof, not for deploying the next app change; see [Package a running service](/zcp/workflows/package-running-service). @@ -32378,8 +32362,8 @@ That is the practical difference between "the agent wrote code" and "the app tas ## Next steps -- [Build and ship](/zcp/workflows/build-with-zcp) - normal app work after setup. -- [Workflows in depth](/zcp/reference/agent-workflow) - process gates, generated files, runtime layouts, delivery terms, and completion evidence. +- [Build and ship](/zcp/workflows/build-with-zcp) — normal app work after setup. +- [Workflows in depth](/zcp/reference/agent-workflow) — process gates, generated files, runtime layouts, delivery labels, and completion evidence. ---------------------------------------- @@ -32395,7 +32379,7 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee **MCP server** - the Model Context Protocol server exposed by the `zcp` binary. -**ZCP MCP tools** - the bounded Zerops operations exposed to an agent or MCP-capable client. In MCP clients, this usually appears as the `zerops` server. +**ZCP MCP tools** - the project-scoped Zerops operations exposed to an agent or MCP-capable client. In MCP clients, this usually appears as the `zerops` server. **`zcp` binary** - the executable that can run inside remote setup or on your machine in local setup. @@ -32445,7 +32429,7 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee **Runtime target** - the app runtime selected for the current change, such as `appdev`, `appstage`, `app`, or a linked local target. -**Runtime layout** - which app runtime services the workflow should use: +**Runtime layout** - which app runtime services the workflow should use: - `standard` - dev runtime plus explicit stage runtime. - `dev` - one mutable development runtime. @@ -32481,7 +32465,7 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee **Package a running service** - workflow that turns one verified runtime and its managed dependencies into a re-importable, git-backed Zerops bundle. -**Production promotion** - handoff from verified dev or stage work into a separate production Zerops project through production infrastructure setup, production deploy trigger setup, and repeatable releases. +**Production release** - the release operation that moves verified dev or stage work into a separate production Zerops project. It is set up once per project (production infrastructure) and once per runtime (production deploy trigger), then runs every release. **Production boundary** - policy that production should live in a separate Zerops project without a `zcp` service and receive promoted work through CI, release process, or human action. @@ -32491,7 +32475,7 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee **Public subdomain access** - Zerops `.zerops.app` URL access for an eligible HTTP runtime. Workers and non-HTTP services do not get useful public URLs. -**Failure category** - label that points to the first useful evidence surface: +**Failure category** - label that points to the first useful evidence surface: - `build` - build phase failed. - `start` - build passed, but runtime start or prepare failed. @@ -32523,23 +32507,28 @@ Use these definitions when a page, workflow status, agent handoff, or policy nee # Zcp > Overview -Zerops provides the project infrastructure: runtimes, managed services, networking, env vars, deploys, logs, and public access. ZCP MCP is the project-scoped tool layer that lets a coding agent work with that infrastructure through MCP. +:::info Public preview +ZCP MCP is a public preview and is under active development. If you hit a bug, confusing behavior, or a missing workflow, please report it on [Discord](/discord); feedback from real projects is especially useful. +::: -For the broader feature concept — why coding agents need project infrastructure rather than a sandbox or generated artifact — start with [Infrastructure for Coding Agents](/features/coding-agents). +This section covers, in order: -With the generated workflow instructions enabled, an app task should end in proof or a blocker. Proof means a deployed runtime plus the URL, endpoint response, UI state, worker result, or stored data that shows the requested behavior works. A blocker means the agent read the relevant Zerops evidence and names the missing credential, decision, unsupported fit, or repeated failure. +- **How a run unfolds.** [Quickstart](/zcp/quickstart) for hands-on; [How it works](/zcp/concept/how-it-works) for the work loop. +- **Where the agent runs.** [Remote or local setup](/zcp/setup/choose-workspace), [Trust model](/zcp/security/trust-model), and [Production boundary](/zcp/security/production-policy). +- **What you decide while working.** [Build and ship](/zcp/workflows/build-with-zcp), [Package a running service](/zcp/workflows/package-running-service), [Promote to production](/zcp/workflows/promote-to-production). +- **Exact labels when they matter.** [Workflows in depth](/zcp/reference/agent-workflow), [ZCP MCP tools](/zcp/reference/mcp-operations), [Troubleshooting](/zcp/reference/troubleshooting), [Glossary](/zcp/glossary). -Your prompt can stay about the product. Name the stack, runtime layout, acceptance criteria, delivery path, external credentials, or risky approval only when those decisions matter. +For the broader feature concept — why coding agents need real project infrastructure rather than a sandbox or generated artifact — start with [Infrastructure for Coding Agents](/features/coding-agents). -:::info Public preview -ZCP MCP is a public preview and is currently under active development. If you hit a bug, confusing behavior, or a missing workflow, please report it on [Discord](/discord); feedback from real projects is especially useful. -::: +With the generated workflow instructions enabled, an app task ends in proof or a blocker. **Proof** is a deployed runtime plus the URL, endpoint response, UI state, worker result, or stored data that shows the requested behavior works. A **blocker** is the agent reading the relevant Zerops evidence and naming the missing credential, decision, unsupported fit, or repeated failure. + +Your prompt can stay about the product. Name the stack, runtime layout, acceptance criteria, delivery path, external credentials, or risky approval only when those decisions matter. ## What the agent gets **Current state.** Services, runtime layout, managed dependencies, env-var keys and references, logs, events, deploy history, verification state, and saved work state. -**Zerops operations.** Tools for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. +**Zerops operations.** Project-scoped tools for discovering services, changing env vars, managing runtimes, deploying, verifying, scaling, public access, and delivery setup. **Workflow.** The generated instructions combine service setup and app development: inspect state, choose the runtime target, use or create services, wire code and `zerops.yaml`, deploy, verify, and choose delivery. The Zerops work stays behind the product task instead of becoming another checklist. @@ -32572,34 +32561,9 @@ To start, add remote setup in Zerops or initialize local setup beside your edito **Workspace freedom.** The `zcp@1` service is still a normal Zerops service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and adapt the workspace. Details live in [What remote workspace gives you](/zcp/setup/hosted-workspace#make-customization-persistent). :::caution Production boundary -Use this setup for development or staging work. Production should stay in a separate Zerops project and receive promoted work through your CI or release process; see [Promote to production](/zcp/workflows/promote-to-production) for the practical flow and [Production boundary](/zcp/security/production-policy) for the policy. +Use this setup for development or staging work. Production should stay in a separate Zerops project and receive released work through your CI or release process; see [Promote to production](/zcp/workflows/promote-to-production) for the practical flow and [Production boundary](/zcp/security/production-policy) for the policy. ::: -## Start here - -First-read pages: - -| If you want to | Read | -| ----------------------------------------- | -------------------------------------------------------- | -| Try the guided hands-on path | [Quickstart](/zcp/quickstart) | -| Understand the agent operating loop | [How it works](/zcp/concept/how-it-works) | -| Compare remote and local setup | [Remote or local setup](/zcp/setup/choose-workspace) | -| Start building after setup | [Build and ship](/zcp/workflows/build-with-zcp) | -| Start building after setup | [Build and ship](/zcp/workflows/build-with-zcp) | - -Specific tasks and reference: - -| If you want to | Read | -| ---------------------------------------- | --------------------------------------------------------------------------- | -| Understand remote workspace | [What remote workspace gives you](/zcp/setup/hosted-workspace) | -| Use your local editor or CLI agent | [Run locally](/zcp/setup/local-agent-bridge) | -| Decide how finished work ships | [Build and ship](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | -| Understand token access and boundaries | [Trust model](/zcp/security/trust-model) | -| Understand the feature concept | [Infrastructure for Coding Agents](/features/coding-agents) | -| Understand workflow-guided runs | [Workflows in depth](/zcp/reference/agent-workflow) | -| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | -| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | - ## What stays outside This section is not a replacement for Zerops platform references. The Zerops [build and deploy pipeline](/features/pipeline), [permissions](/features/rbac), networking, scaling, and service references remain canonical for platform behavior. @@ -32678,9 +32642,9 @@ If the agent cannot finish, useful output names the blocker: missing credential, ## Next steps +- [Build and ship](/zcp/workflows/build-with-zcp) — Runtime layout, development work, delivery, packaging, and production release. - [How it works](/zcp/concept/how-it-works) — Live state, runtime fit, app wiring, deploy evidence, behavior proof, and blockers. -- [Remote or local setup](/zcp/setup/choose-workspace) — Choose whether the agent workspace runs inside Zerops or beside your local files and tools. -- [Build and ship](/zcp/workflows/build-with-zcp) — Runtime layout, development work, delivery, packaging, and production promotion. +- [Trust model](/zcp/security/trust-model) — What the project-scoped token lets the agent do, and where the safety boundary sits. ---------------------------------------- @@ -32688,7 +32652,7 @@ If the agent cannot finish, useful output names the blocker: missing credential, # Zcp > Reference > Agent Workflow -Use this page when you want to understand the process behind workflow-guided agent work. Normal prompts should still describe outcomes. The workflow exists so the agent can read the project, prepare the right runtime and services, make the app change, deploy, verify, recover from evidence, and stop with proof or a concrete blocker. +Use this page when you want the process and exact labels behind a run that follows the generated workflow. Normal prompts should still describe outcomes. The workflow exists so the agent can read the project, prepare the right runtime and services, make the app change, deploy, verify, recover from evidence, and stop with proof or a concrete blocker. The useful mental split is: @@ -32698,7 +32662,7 @@ The useful mental split is: | **Develop** | Code, `zerops.yaml`, env wiring, deploy, verification, recovery, and delivery choice. | The requested behavior is proved, or the blocker is concrete. | | **Develop** | Code, `zerops.yaml`, env wiring, deploy, verification, recovery, and delivery choice. | The requested behavior is proved, or the blocker is concrete. | -Bootstrap is not a marketing or onboarding step. It is the workflow's name for "make the project layout safe to work in before changing app code." +For the loop itself, see [How it works](/zcp/concept/how-it-works). This page catalogs the exact phases, routes, layouts, labels, and gates. ## Session layers @@ -32715,7 +32679,7 @@ The `zcp` service is the control surface, not the app runtime. ## What drives the workflow -The workflow-guided experience is made from four pieces: +The generated workflow is made from four pieces: | Piece | Role | | ----- | ---- | @@ -32767,44 +32731,13 @@ Bootstrap ends when the app runtime target and managed dependencies are known. I ## Runtime layouts -Runtime layout describes which app runtime services the workflow should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. - -| Layout | Meaning | Typical names | -| ------ | ------- | ------------- | -| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage is explicit. | `appdev` + `appstage` | -| `dev` | One mutable development runtime. | `appdev` | -| `simple` | One runtime with no dev/stage split. | `app` | -| `local-stage` | Local source directory linked to one Zerops runtime for deploys. | local directory + `appstage` | -| `local-only` | Local source directory with no linked deploy target yet. | local directory | -| `local-only` | Local source directory with no linked deploy target yet. | local directory | +Runtime layout describes which app runtime services the workflow should use. Exact layout labels (`standard`, `dev`, `simple`, `local-stage`, `local-only`) and the runtime names they map to live in the [Glossary](/zcp/glossary#runtime-layout). The user-facing three (dev, dev + stage, stage / linked target) are in [Build and ship → Choose the runtime layout](/zcp/workflows/build-with-zcp#choose-the-runtime-layout). Service scaling mode (`HA`/`NON_HA`) is a separate service setting. -In `standard`, stage is explicit. Work scoped to `appdev` does not silently touch `appstage`; promotion or stage verification happens when the user asks for it. +In `standard`, stage is explicit. Work scoped to `appdev` does not silently touch `appstage`; a release or stage verification happens when the user asks for it. ## Develop -Develop is the main app-work loop. It begins after bootstrap has a runtime target and dependencies. It closes only when runtime reachability and requested behavior both pass, or when the agent has a blocker that needs a human decision. - -```mermaid -flowchart TD - scope["1. Name runtime target"] - change["2. Change code/config"] - deploy["3. Direct deploy"] - serve["4. Start or restart if needed"] - reach{"5. Runtime reachable?"} - behavior{"6. Requested behavior works?"} - done(["Proof"]) - fix["7. Categorize failure -read evidence, fix"] - blocker(["Blocker"]) - - scope --> change --> deploy --> serve --> reach - reach -- yes --> behavior - reach -- no --> fix - behavior -- yes --> done - behavior -- no --> fix - fix --> deploy - fix --> blocker -``` +Develop is the main app-work loop. It begins after bootstrap has a runtime target and dependencies. It closes only when runtime reachability and requested behavior both pass, or when the agent has a blocker that needs a human decision. The loop itself is in [How it works → The work loop](/zcp/concept/how-it-works#the-work-loop). The labeled steps are: 1. **Name runtime target.** State which runtime is in scope: `appdev`, `appstage`, `app`, or a linked local target. 2. **Change code and config.** Edit app files, `zerops.yaml`, env references, migrations, seeds, framework config, or local `.env` bridge when needed. @@ -32818,20 +32751,9 @@ Reachability and requested behavior are separate gates. A green deploy with a br ## Failure categories -Failure categories point the agent to the first useful evidence surface. +Failure categories (`build`, `start`, `verify`, `network`, `config`, `credential`, `other`) point the agent to the first useful evidence surface. The full read-first / avoid playbook is in [Troubleshooting → Evidence order](/zcp/reference/troubleshooting#evidence-order); the term definitions are in the [Glossary](/zcp/glossary#failure-category). -| Category | What it means | Read first | -| -------- | ------------- | ---------- | -| `build` | Build phase failed. | Build logs, build commands, dependency manifests, deploy file list. | -| `start` | Build passed, but runtime start or prepare failed. | Prepare/runtime logs, start command, ports, env references. | -| `verify` | Runtime exists, but reachability or behavior failed. | Failing check detail, HTTP response, request-time runtime logs, behavior evidence. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | VPN, SSH, DNS, subdomain readiness, service status, named transport error. | -| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Field-level rejection, setup name, env reference, service settings. | -| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | -| `other` | No known category matched. | Raw event/log evidence; stop if repeated without a new signal. | -| `other` | No known category matched. | Raw event/log evidence; stop if repeated without a new signal. | - -Categorization is what turns retries into evidence-driven fixes. The practical recovery flow lives in [Troubleshooting](/zcp/reference/troubleshooting). +Categorization is what turns retries into evidence-driven fixes. ## Delivery after proof @@ -32846,7 +32768,7 @@ Delivery mode applies after a verified deploy. It does not replace the first pro Git-push capability, delivery mode, and build integration are separate. A project can have git-push configured while still using direct deploy for a given session. -Packaging and production promotion are deliberate handoffs after proof. Packaging turns a verified runtime into a git-backed import bundle. Production promotion moves verified work into a separate production project through GUI setup, git/CI triggers, or the team's release process. +Packaging and production release are deliberate handoffs after proof. Packaging turns a verified runtime into a git-backed import bundle. A production release moves verified work into a separate production project through GUI setup, git/CI triggers, or the team's release process. ## Generated files and state @@ -32887,16 +32809,11 @@ A clear blocker is acceptable completion only when it names the runtime in scope ## Confirmation gates -Some operations pause because the loss is not safely reversible from inside the conversation: - -- **Service deletion** requires explicit user approval in the current conversation, by service name. -- **Destructive import override** first refuses and names what would be replaced; a second call must acknowledge the same targets. - -See [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) for the user-facing confirmation flow. +Two operations pause because the loss is not safely reversible from inside the conversation: **service deletion** (named approval) and **destructive import override** (refuse-then-acknowledge). See [Tokens and credentials → What ZCP enforces for destructive actions](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) for the user-facing confirmation flow. ## Auditing a workflow -A workflow-guided run is well-shaped if the evidence answers: +A workflow run is well-shaped if the evidence answers: | Question | Evidence | | -------- | -------- | @@ -32905,14 +32822,14 @@ A workflow-guided run is well-shaped if the evidence answers: | Which runtime was developed and deployed? | Runtime target, deploy result, events, and logs. | | What proved reachability? | Verify output, service status, public URL, or HTTP probe. | | What proved behavior? | Endpoint, UI flow, job result, database/object state, or other requested proof. | -| What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production handoff note. | -| What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production handoff note. | +| What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production release handoff note. | +| What controls future delivery? | Delivery mode, git-push state, build integration, package bundle, or production release handoff note. | ## Related reference -- [ZCP MCP tools](/zcp/reference/mcp-operations) - operation names and direct tool calls. -- [Troubleshooting](/zcp/reference/troubleshooting) - recovery order when a run gets stuck. -- [Glossary](/zcp/glossary) - exact terms used across the ZCP MCP reference. +- [ZCP MCP tools](/zcp/reference/mcp-operations) — operation names and direct tool calls. +- [Troubleshooting](/zcp/reference/troubleshooting) — recovery order when a run gets stuck. +- [Glossary](/zcp/glossary) — exact terms used across the ZCP MCP reference. ---------------------------------------- @@ -32920,15 +32837,48 @@ A workflow-guided run is well-shaped if the evidence answers: # Zcp > Reference > Index -Use reference pages when exact labels matter. Day-to-day app work starts in [Build and ship](/zcp/workflows/build-with-zcp). +Reference pages exist for the moments when an exact label, tool name, or recovery step matters. For day-to-day app work, start in [Build and ship](/zcp/workflows/build-with-zcp); for the loop itself, [How it works](/zcp/concept/how-it-works). + +- [Workflows in depth](/zcp/reference/agent-workflow) — Catalog of phases, routes, layouts, and labels behind a generated-workflow run. +- [ZCP MCP tools](/zcp/reference/mcp-operations) — Exact tool names and surface for custom MCP clients or agent policy. +- [Troubleshooting](/zcp/reference/troubleshooting) — When the agent is stuck, a session was interrupted, deploys keep failing, or you are taking over manually. +- [Glossary](/zcp/glossary) — Definitions for terms across pages and workflow status (`appdev`, `local-stage`, `delivery mode`, etc.). + +## Common scenarios + +
+ +
+ +SCENARIO + +### Debugging a stuck deploy + +[Troubleshooting → Evidence order](/zcp/reference/troubleshooting#evidence-order) names what to read first; [Workflows in depth → Failure categories](/zcp/reference/agent-workflow#failure-categories) maps the labels. + +
+ +
+ +SCENARIO -| Need | Page | -|---|---| -| Understand what sits behind workflow-guided agent runs: generated instructions, state, process gates, and completion evidence | [Workflows in depth](/zcp/reference/agent-workflow) | -| Use ZCP MCP tools directly or look up operation names for a custom MCP integration | [ZCP MCP tools](/zcp/reference/mcp-operations) | -| Recover a stuck or confused session | [Troubleshooting](/zcp/reference/troubleshooting) | -| Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | -| Disambiguate ZCP, remote setup, local setup, `zcp`, zCLI, and credential names | [Glossary](/zcp/glossary) | +### Building a custom MCP client + +[ZCP MCP tools](/zcp/reference/mcp-operations) for the tool surface; [Workflows in depth → What drives the workflow](/zcp/reference/agent-workflow#what-drives-the-workflow) for what the generated workflow adds on top of bare tools. + +
+ +
+ +SCENARIO + +### Auditing a finished run + +[Workflows in depth → Completion evidence](/zcp/reference/agent-workflow#completion-evidence) for what should be in the final answer; [Trust model → Audit evidence](/zcp/security/trust-model#audit-evidence) for what Zerops records platform-side. + +
+ +
---------------------------------------- @@ -32944,7 +32894,7 @@ ZCP MCP is the MCP server exposed by the `zcp` binary. Any MCP-capable client ca The MCP server exposes operations. It does not, by itself, decide the whole app lifecycle. -Workflow-guided Claude Code uses generated instructions to decide sequencing: inspect state before changing things, choose the runtime target, use existing services or create missing ones, deploy, read failure evidence, verify requested behavior, and stop with proof or a blocker. +Claude Code with the generated workflow uses its instructions to decide sequencing: inspect state before changing things, choose the runtime target, use existing services or create missing ones, deploy, read failure evidence, verify requested behavior, and stop with proof or a blocker. Tool-only use is valid when you want a custom MCP client, script, dashboard, policy-gated agent, or narrow operational task. In that setup, your integration owns the sequencing: @@ -33036,7 +32986,7 @@ See [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-en ## Related reference -- [Workflows in depth](/zcp/reference/agent-workflow) - how workflow-guided runs use these tools. +- [Workflows in depth](/zcp/reference/agent-workflow) - how a generated-workflow run uses these tools. - [Troubleshooting](/zcp/reference/troubleshooting) - evidence order when a run gets stuck. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, and confirmation gates. @@ -33149,7 +33099,7 @@ Use the MCP setup in development or staging. Keep production in a separate Zerop Zerops does not prevent you from adding `zcp` to production. The policy exists because ZCP MCP gives a coding agent operational access. In production, that is the wrong blast radius for normal app development. -The production boundary does not make ZCP output disposable. The intended path is production-shaped dev or stage infrastructure, verified behavior, and then a controlled production promotion outside the agent loop. +The production boundary does not make ZCP output disposable. The intended path is production-shaped dev or stage infrastructure, verified behavior, and then a controlled production release outside the agent loop. ## Recommended project layout @@ -33172,7 +33122,7 @@ This separation matters even when the same source repository deploys to both pro ## What stage proves -Stage is the production-like rehearsal inside development or staging. It is where the agent proves the change before promotion leaves the ZCP loop. +Stage is the production-like rehearsal inside development or staging. It is where the agent proves the change before the release leaves the ZCP loop. Use stage to match production where it matters: @@ -33184,9 +33134,9 @@ Use stage to match production where it matters: Stage is not production. It should use non-production data and non-production secrets. A green stage means the change is ready for release, not that the agent should deploy to production itself. -## Promotion +## The release -After stage verifies, ZCP's job is done for that change. Promotion happens outside the agent loop: +After stage verifies, ZCP's job is done for that change. The production release happens outside the agent loop: - CI deploys a merged commit or release tag to the production project. - A release pipeline runs `zcli push` with a production-scoped `ZEROPS_TOKEN`. @@ -33221,7 +33171,7 @@ If a production deploy fails, investigate in the production project with product ## Acceptable agent involvement -The agent can still help before production promotion: +The agent can still help before the production release: - make the code change in development, - deploy and verify dev/stage runtimes, @@ -33233,7 +33183,7 @@ ZCP's involvement stops at the handoff. Production execution belongs to the rele ## Related security -- [Promote to production](/zcp/workflows/promote-to-production) - practical production promotion paths after ZCP proof. +- [Promote to production](/zcp/workflows/promote-to-production) - practical production release paths after ZCP proof. - [Trust model](/zcp/security/trust-model) - the boundary that makes this policy enforceable. - [Tokens and credentials](/zcp/security/tokens-and-project-access) - production and development credentials stay separate. @@ -33457,9 +33407,7 @@ There are three separate credential surfaces people often mix together: | Git or CI credentials | You / your repository system | Lets finished work push to git or deploy through CI. | | Git or CI credentials | You / your repository system | Lets finished work push to git or deploy through CI. | -Remote setup injects `ZCP_API_KEY` into the `zcp` service. Local setup reads it from the `.mcp.json` env block. In both paths, the agent account is still authenticated through the agent's own login flow. - -Details: [Tokens and credentials](/zcp/security/tokens-and-project-access). +In both setups, the agent account is still authenticated through the agent's own login flow. For where each credential lives (remote injection vs `.mcp.json`), rotation, rejected token shapes, and `GIT_TOKEN` / `ZEROPS_TOKEN`, see [Tokens and credentials](/zcp/security/tokens-and-project-access). ## What the token lets the agent do @@ -33494,15 +33442,7 @@ This is why development and staging projects are the right place for ZCP. Produc Most project operations do not get an extra ZCP-specific confirmation prompt. Deploys, env changes, restarts, scaling, and public-access changes are normal project operations and are audited through platform evidence. -ZCP MCP adds hard gates where the loss is not safely reversible from the conversation: - -| Operation | Gate | -| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -| Service deletion | Requires explicit same-conversation approval that names the service. Remote setup also blocks deleting the `zcp` service it is running in. | -| Wholesale service replacement after failed deploy history | Requires the agent to read failure evidence first, summarize what would be replaced, and ask for explicit confirmation. | -| Wholesale service replacement after failed deploy history | Requires the agent to read failure evidence first, summarize what would be replaced, and ask for explicit confirmation. | - -Approval from an old chat does not carry forward. A new conversation needs a new approval. +ZCP MCP adds hard gates where the loss is not safely reversible from the conversation: **service deletion** (explicit same-conversation approval by service name; remote setup also blocks deleting the `zcp` service it is running in) and **wholesale service replacement after failed deploy history** (refuse-then-acknowledge with failure evidence first). Approval from an old chat does not carry forward. The full enforcement rules are in [Tokens and credentials → What ZCP enforces for destructive actions](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions). ## Audit evidence @@ -33627,7 +33567,7 @@ Taking over is straightforward in this setup. You open the same workspace, termi ## What it includes -- **`zcp@1` workspace service.** Runs ZCP inside Zerops and gives the agent bounded operations. +- **`zcp@1` workspace service.** Runs ZCP inside Zerops and gives the agent project-scoped operations. - **Platform-injected `ZCP_API_KEY`.** Zerops injects the token into the workspace; you normally do not set it by hand. - **Bundled Claude Code when enabled.** The **Include Coding Agent** option installs and preconfigures Claude Code. You authenticate with your own Claude subscription login or API key. - **Browser VS Code when enabled.** The **Cloud IDE** option gives you a browser editor, terminal, and a place to supervise or take over the agent session. @@ -33707,8 +33647,8 @@ Tools installed inside the `zcp` service may see workspace environment variables | `ZCP_API_KEY` | Injected by Zerops into the `zcp` workspace. | | Agent account | Your Claude subscription login or model API credential. Zerops wires the agent to ZCP, but the agent account remains yours. | | Git credentials | Configured inside the workspace when the agent should commit or push from remote setup. | -| Production promotion | A separate production project and release process. | -| Production promotion | A separate production project and release process. | +| Production release | A separate production project and release process. | +| Production release | A separate production project and release process. | ## Runtime file access @@ -33879,7 +33819,7 @@ Keep `.env` out of git. It contains real connection values. ## 8. Link a deploy target -Local setup needs a Zerops runtime when the agent should deploy from your working directory. A stage runtime is the usual target because it lets local work prove itself before production promotion. +Local setup needs a Zerops runtime when the agent should deploy from your working directory. A stage runtime is the usual target because it lets local work prove itself before the production release. If there is exactly one runtime, the agent can use it automatically. If multiple runtimes exist, it should ask which one to link: @@ -33911,18 +33851,6 @@ The MCP server does not own your git credentials. In local setup, your local git - [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and destructive confirmations. ----------------------------------------- - -# Zcp > Workflows > Build And Verify App - - -This page moved to [Build and ship](/zcp/workflows/build-with-zcp). - -- Use [Development](/zcp/workflows/build-with-zcp#develop-with-live-project-context) for app work, deploy, and verification. -- Use [How it works -> Verification has two layers](/zcp/concept/how-it-works#verification-has-two-layers) for the deeper verification model. -- Use [Troubleshooting](/zcp/reference/troubleshooting) for symptoms and recovery moves. - - ---------------------------------------- # Zcp > Workflows > Build With Zcp @@ -33935,7 +33863,7 @@ Build a task board. Tasks should stay saved after refresh. ``` -A prompt can be that short when the outcome is enough. Add detail when it changes behavior, architecture, stack, runtime layout, acceptance criteria, credentials, delivery, packaging, or production promotion. +A prompt can be that short when the outcome is enough. Add detail when it changes behavior, architecture, stack, runtime layout, acceptance criteria, credentials, delivery, packaging, or the production release. The expected output is a verified running change, not only generated files. The agent should prove the request against a real runtime, real managed services when used, and the logs, events, and checks that explain what happened. @@ -33967,7 +33895,7 @@ The expected output is a verified running change, not only generated files. The
  • Direct deploy, git, or CI
  • Optional package bundle
  • -
  • Production promotion
  • +
  • Production release
@@ -33984,7 +33912,7 @@ The main user-facing choice is runtime layout. Let the agent infer it from the p | Runtime layout | Use when | What to tell the agent | | ------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------- | | **Dev** | You want one mutable runtime for fast iteration, experiments, or early app work. | `Build a small Node.js API on dev.` | -| **Dev + stage** | You want a development runtime plus a separate runtime for review or promotion rehearsal. | `Build a Node.js API with PostgreSQL on dev+stage.` | +| **Dev + stage** | You want a development runtime plus a separate runtime for review or release rehearsal. | `Build a Node.js API with PostgreSQL on dev+stage.` | | **Stage / linked target** | You work from local files or a single target runtime and want the workflow to use that target. | `Use appstage as the deploy target for this local app.` | | **Stage / linked target** | You work from local files or a single target runtime and want the workflow to use that target. | `Use appstage as the deploy target for this local app.` | @@ -34038,9 +33966,9 @@ The agent may still ask which runtime to package, which half of a dev+stage pair Packaging is not the next deploy of the same app. For the next app change, keep using the normal build/deploy/verify loop. Use packaging when the output you want is a git-backed import bundle; see [Package a running service](/zcp/workflows/package-running-service). -## 5. Prepare production promotion {#prepare-production-promotion} +## 5. Prepare the production release {#prepare-production-release} -Production promotion is where authority changes. The agent prepares verified work and release evidence; production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. +The production release is where authority changes. The agent prepares verified work and release evidence; production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. The useful split is: @@ -34061,33 +33989,11 @@ For a completed app task, the agent should report: - the deploy or verification target, - the URL, endpoint, UI state, job result, or stored data that proves the requested behavior, - managed services, env vars, or delivery settings it touched, -- the delivery preference, packaging output, or production promotion state that now applies. +- the delivery preference, packaging output, or production release state that now applies. If the task is incomplete, the final answer should name the blocker, the evidence read, what was tried, and the decision or credential needed from you. ----------------------------------------- - -# Zcp > Workflows > Create Or Adopt Services - - -This page moved to [Build and ship](/zcp/workflows/build-with-zcp). - -- Use [Runtime layout](/zcp/workflows/build-with-zcp#choose-the-runtime-layout) for existing services, missing services, and the handoff from setup into app work. -- Use [Workflows in depth](/zcp/reference/agent-workflow) for workflow process details and exact labels. - - ----------------------------------------- - -# Zcp > Workflows > Delivery Handoff - - -This page moved to [Build and ship](/zcp/workflows/build-with-zcp). - -- Use [Delivery after proof](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) for the user-facing choices: keep direct deploy, push verified work to git, or hand off to CI/release tooling. -- Use [Workflows in depth](/zcp/reference/agent-workflow#delivery-after-proof) for delivery labels and workflow context. - - ---------------------------------------- # Zcp > Workflows > Package Running Service @@ -34118,7 +34024,7 @@ Package a runtime when you need: Do not use packaging for: - the next app deploy; use the normal build, deploy, and verify loop, -- production promotion; production needs its own infrastructure, credentials, domains, and release trigger, +- the production release; production needs its own infrastructure, credentials, domains, and release trigger, - moving managed-service data; backups and restores are separate. For production setup, follow [Promote to production](/zcp/workflows/promote-to-production). Packaging can help create a reusable app bundle, but it does not decide production infrastructure or release policy. @@ -34178,7 +34084,7 @@ Before opening the new project to users: # Zcp > Workflows > Promote To Production -Use this after the agent has proved work in development or stage. Production promotion is the point where authority changes: the agent can prepare proof, source changes, and setup notes, but production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. +Use this after the agent has proved work in development or stage. The production release is the point where authority changes: the agent can prepare proof, source changes, and setup notes, but production execution belongs to Zerops project settings, CI, release tooling, or a deliberate human action with production credentials. Production should be a separate Zerops project without a `zcp` service. That keeps the development agent out of the production blast radius while still letting verified work reach production.

l!<)q&SPt1w5J0I-d0E-HCxlYF@XcZYuZ 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 YolM$3G^(VSOhOz+H%)F-={iOK2QcbMPyhe` literal 0 HcmV?d00001 diff --git a/apps/docs/static/img/zcp/quickstart-zcp-service-ready.png b/apps/docs/static/img/zcp/quickstart-zcp-service-ready.png new file mode 100644 index 0000000000000000000000000000000000000000..bae7d9514e182c6d2c467d6fd218eea02a205d1e GIT binary patch literal 37871 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 Fe*=me6qNt~ literal 0 HcmV?d00001 From 514c96a2f39c60eea13bdba6881397db4ff85307 Mon Sep 17 00:00:00 2001 From: krls2020 Date: Sun, 10 May 2026 16:38:59 +0200 Subject: [PATCH 06/24] Refine ZCP guide structure and setup framing --- apps/docs/content/features/coding-agents.mdx | 54 +- .../docs/content/zcp/concept/how-it-works.mdx | 194 +- apps/docs/content/zcp/glossary.mdx | 110 +- apps/docs/content/zcp/overview.mdx | 36 +- .../content/zcp/reference/agent-workflow.mdx | 82 +- .../zcp/security/production-policy.mdx | 99 +- .../security/tokens-and-project-access.mdx | 167 +- .../docs/content/zcp/security/trust-model.mdx | 147 +- .../content/zcp/setup/choose-workspace.mdx | 106 +- .../content/zcp/setup/hosted-workspace.mdx | 157 +- .../content/zcp/setup/local-agent-bridge.mdx | 153 +- .../zcp/workflows/build-and-verify-app.mdx | 60 +- .../content/zcp/workflows/build-with-zcp.mdx | 54 +- .../workflows/create-or-adopt-services.mdx | 47 +- .../zcp/workflows/delivery-handoff.mdx | 40 +- .../zcp/workflows/package-running-service.mdx | 62 +- apps/docs/sidebars.js | 3675 ++++++++--------- 17 files changed, 2788 insertions(+), 2455 deletions(-) diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx index aa5e86aad..37bec9ad7 100644 --- a/apps/docs/content/features/coding-agents.mdx +++ b/apps/docs/content/features/coding-agents.mdx @@ -17,20 +17,20 @@ ZCP runs with a project-scoped token that grants operational rights inside that Developers using a coding agent on application work that needs real infrastructure. The developer stays focused on the product outcome, technology choices, acceptance criteria, and judgment. ZCP gives the agent the platform context and operations needed to use or create services, connect the app to infrastructure, deploy, verify, recover from failure, and report back. -The **Include Coding Agent** option bundles the currently supported agent CLI shown in the dashboard and wires it to ZCP. The agent account remains yours: authenticate it with your own subscription login or API credentials. Local setup can connect a compatible local agent client after `zcp init`. +The **Include Coding Agent** option currently bundles Claude Code and wires it to ZCP. The agent account remains yours: authenticate it with your own subscription login or API credentials. Additional bundled agents are planned. Local setup can connect a compatible local agent client after `zcp init`. ## What ZCP lets the agent do ZCP exposes a fixed set of operations on one Zerops project, grouped by user job. The prompt can stay focused on the app because the agent does not need you to describe what the project looks like - ZCP reports it from what is 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 | [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) | -| **Verify** | Reachability + behavior checks against the actual URL | [Deploy, verify, and fix](/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) | -| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | +| 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 | [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) | +| **Verify** | Reachability + behavior checks against the actual URL | [Deploy, verify, and fix](/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) | +| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | Behind one app request, the agent should read the project, use existing services or create missing ones, edit and deploy the app, verify the requested behavior, and then record how future changes should ship. A green deploy with a 500 on the relevant route is not done — verification has two layers and both must pass. @@ -57,15 +57,15 @@ Decision: [Choose remote or local setup](/zcp/setup/choose-workspace). First tim Remote setup starts with a `zcp@1` Zerops service. Two dashboard options decide what is bundled into that service: -**Include Coding Agent.** Adds the currently supported bundled agent CLI and preconfigures it to use ZCP running inside the `zcp@1` service. Agent authentication is handled separately by that agent's login method, subscription, or API key. +**Include Coding Agent.** Adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use ZCP running inside the `zcp@1` service. Agent authentication is handled separately by that agent's login method, subscription, or API key. **Cloud IDE.** Browser-based VS Code (code-server) with 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 → Cloud IDE](/features/local-remote-development#cloud-ide-on-remote-setup). Without **Include Coding Agent**, the service can still exist, but there is no preconfigured agent waiting inside it. Without **Cloud IDE**, there is no browser editor. With both enabled, you get a one-click agent + IDE setup inside the project. -Both options are configured when you provision the service: [Set up remote ZCP → Path B](/zcp/setup/hosted-workspace#path-b--toggle-the-zcp-service). +Both options are configured when you provision the service: [Use remote ZCP workspace -> Configure the ZCP service](/zcp/setup/hosted-workspace#configure-the-zcp-service). -Remote setup is still your workspace. You can install another agent CLI, add private MCP servers or helper processes, edit `CLAUDE.md` or other agent instruction files, and keep team tooling in the service. ZCP supplies the Zerops project API and workflow evidence; it does not lock the service to one agent or one set of tools. +Remote setup is still your workspace. You can install another agent CLI, add private MCP servers or helper processes, edit `CLAUDE.md` or other agent instruction files, and keep team tooling in the service. ZCP supplies the project-scoped control plane, operations, and workflow evidence; it does not lock the service to one agent or one set of tools. ## Why transparent infrastructure works for agents @@ -101,13 +101,13 @@ Full operation list and permission semantics: [Advanced operations](/zcp/referen ## Customize remote setup -Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product - install packages, edit agent instructions, configure additional MCP servers, run helper processes alongside the agent, or ship a hardened version with team-standard tools baked in. Patterns: [Customize remote setup](/zcp/setup/hosted-workspace#customize-remote-setup). +Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated when you enable **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product - install packages, edit agent instructions, configure additional MCP servers, run helper processes alongside the agent, or ship a hardened version with team-standard tools baked in. Patterns: [Make customization persistent](/zcp/setup/hosted-workspace#make-customization-persistent). -Local setup isn't extensible the same way; the binary on your laptop is just ZCP - editor, shell, and dev server stay your tools' job. +Local setup isn't extensible the same way; the binary on your laptop is just ZCP - editor, shell, local files, local data, and tool feedback stay on your machine. ## Source control -When the agent runs in remote setup, 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. In local setup, 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 remote setup, 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. In local setup, it uses your existing git credentials. Detail: [Tokens and credentials -> Git and CI credentials](/zcp/security/tokens-and-project-access#git-and-ci-credentials). ## What ZCP is not @@ -117,22 +117,22 @@ ZCP is not a prompt-to-prototype generator, an isolated code sandbox, or an edit ZCP is closest to the tools that let an agent work on real software over time, but the center of gravity is different: the agent operates one real Zerops project rather than a preview sandbox or editor-only workspace. -| Category | What it optimizes for | Where ZCP differs | -|---|---|---| -| Prompt-to-prototype tools | Fast artifact generation and visual preview. | ZCP targets a running app on real services, with deploy, logs, persistence, and verification. | -| Code execution sandboxes | Safe isolated execution. | ZCP gives the agent managed services, private networking, deployment, and project state that persist across sessions. | -| AI-enabled cloud IDEs | Editing code in a hosted development environment. | ZCP gives the agent platform operations too: create/use services, wire env vars, deploy, verify, recover, and hand off. | -| Primitive/API collections | Calling many specialized APIs. | ZCP keeps the app and dependencies in one Zerops project, reachable by service names and standard platform mechanics. | +| Category | What it optimizes for | Where ZCP differs | +| ------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| Prompt-to-prototype tools | Fast artifact generation and visual preview. | ZCP targets a running app on real services, with deploy, logs, persistence, and verification. | +| Code execution sandboxes | Safe isolated execution. | ZCP gives the agent managed services, private networking, deployment, and project state that persist across sessions. | +| AI-enabled cloud IDEs | Editing code in a hosted development environment. | ZCP gives the agent platform operations too: create/use services, wire env vars, deploy, verify, recover, and hand off. | +| Primitive/API collections | Calling many specialized APIs. | ZCP keeps the app and dependencies in one Zerops project, reachable by service names and standard platform mechanics. | The claim is not that ZCP replaces those tools. It solves the missing operational layer when the output must become a verified app running on Zerops infrastructure. ## Where to start diff --git a/apps/docs/content/zcp/concept/how-it-works.mdx b/apps/docs/content/zcp/concept/how-it-works.mdx index 2e18d85b5..6d140c7e2 100644 --- a/apps/docs/content/zcp/concept/how-it-works.mdx +++ b/apps/docs/content/zcp/concept/how-it-works.mdx @@ -1,33 +1,41 @@ --- -title: "How ZCP works" -description: "How product intent moves through live project state, service fit, app wiring, deploy, verification, proof, and delivery." +title: 'How ZCP works' +description: 'The execution model behind ZCP: project state, runtime scope, service setup, app work, verification, recovery, and delivery.' --- -ZCP does not make you run a workflow by hand. It gives the coding agent a project loop: read the current Zerops project, fit the requested app change to the right services, wire the app, deploy it, verify real behavior, and either return proof or stop on a blocker that needs human judgment. +ZCP gives a coding agent an operating loop for one Zerops project. The agent starts from live project state, chooses the app runtime and dependencies, makes the application change, deploys through Zerops, verifies real behavior, and then returns proof or a concrete blocker. -## The loop +The loop is carried by three things: project-scoped tools, Zerops-specific knowledge, and workflow instructions that tell the agent what to inspect, what it can change, when it should ask, and what counts as done. The point is not that you run a workflow by hand. The point is that the agent has a project-aware path behind a product request and can make the important decisions visible while it works. + +## The control loop ```mermaid flowchart TD intent["Product intent
Build a task board for my team."] - state["Project state
services, runtime layout, env vars,
logs, events, current work"] - fit["Service and runtime fit
use existing services or create missing ones;
target the app runtime, not zcp"] - wiring["App wiring
code, zerops.yaml, env references,
managed-service connections"] - deploy["Deploy through Zerops"] - reachability{"Platform reachability
service status, logs, HTTP check"} - behavior{"Requested behavior
endpoint, UI flow, persisted state"} - evidence["Read evidence
build logs, runtime logs,
events, checks"] - proof["Proof
URL, endpoint result,
or UI state"] - blocker["Blocker
missing credential, decision,
unsupported fit, repeated failure"] - delivery["Delivery choice
direct deploy, git push,
or CI/human handoff"] - - intent --> state --> fit --> wiring --> deploy --> reachability - reachability -->|fixable| evidence --> wiring - reachability -->|reachable| behavior - behavior -->|fixable| evidence - behavior -->|verified| proof --> delivery - behavior -->|needs human| blocker + state["Live project state
services, runtime layout, env vars,
logs, events, saved work state"] + scope["Runtime scope
which app service changes"] + setup{"Missing or unsuitable
services?"} + provision["Service setup
use existing services or create
missing runtimes/dependencies"] + appwork["App work
code, zerops.yaml, env refs,
migrations, seeds, framework config"] + deploy["Direct deploy through Zerops"] + reachability{"Runtime reachable?
status, logs, HTTP probe"} + behavior{"Requested behavior works?
endpoint, UI flow, worker result,
persisted state"} + evidence["Evidence
build logs, runtime logs,
events, verify output"] + proof["Proof
URL, endpoint result,
UI state, or stored result"] + delivery["Delivery after proof
keep direct deploy, push to git,
or hand off"] + blocker["Blocker
credential, decision,
unsupported fit, repeated failure"] + + intent --> state --> scope --> setup + setup -->|yes| provision --> appwork + setup -->|no| appwork + appwork --> deploy --> reachability + reachability -->|no, fixable| evidence --> appwork + reachability -->|yes| behavior + behavior -->|no, fixable| evidence + behavior -->|yes| proof --> delivery + setup -->|needs decision| blocker reachability -->|needs human| blocker + behavior -->|needs human| blocker classDef user fill:#f7fbff,stroke:#2d72d9,stroke-width:1.5px,color:#172033; classDef zcpbox fill:#eef7f1,stroke:#32845a,stroke-width:1.5px,color:#172033; @@ -36,84 +44,146 @@ flowchart TD classDef stop fill:#fff1f2,stroke:#d33f49,stroke-width:1.5px,color:#172033; class intent user; - class state zcpbox; - class fit,wiring,deploy,reachability,behavior,evidence work; + class state,scope zcpbox; + class setup,provision,appwork,deploy,reachability,behavior,evidence work; class proof,delivery done; class blocker stop; ``` -The agent should make this path visible while it works, but you should not have to prompt each step. ZCP exists so the operational path is available behind a product request. +The loop gives the agent a way to resolve each phase from project evidence instead of from a guessed checklist. It knows where to read current state, which operations are scoped to the project, which Zerops rules apply to app wiring, which evidence to read after deploy, and when the result is proof versus a blocker. -## The three parts +## What "project state" means -ZCP separates an app task into three parts. These are not checklist items you run by hand; they are the shape of the work the agent should make visible while it operates the project. +ZCP reads the project instead of asking you to paste a service inventory into the prompt. Useful state includes: -**Infrastructure setup.** Establish where the app should run and what it depends on. The agent should use existing services when they fit, create missing runtimes or managed dependencies when they do not, and confirm the app target before writing code. This part is not where app code, `zerops.yaml`, or the first deploy belongs. +- runtime services and managed services, +- whether the app has one runtime, a dev+stage pair, or local files linked to a Zerops runtime, +- env-var keys and Zerops references, +- recent build/deploy events, +- build logs, runtime logs, and verification output, +- saved work state after an interrupted session. -**Development and verification.** Change code and configuration, deploy the selected runtime services, verify platform reachability, verify the requested behavior, read logs and events on failure, fix from evidence, and repeat until proof or blocker. +This is why a short prompt can be enough. The agent can ask ZCP what exists, which runtime was last deployed, which checks passed, and where a previous run stopped. -**Delivery after proof.** After a verified running result exists, choose how future changes should ship: keep direct deploys, push to git, or hand off to CI or a human release process. The first functional deploy is direct; delivery setup follows proof. +Chat history is not the source of truth. If the agent sounds confused, starts from an old assumption, or a session was interrupted, the recovery move is: -## 1. Read the project before choosing a plan +```text +Read ZCP status and tell me where this project stands before changing anything. +``` -The first useful move is discovery. ZCP reads live project state instead of relying on a long prompt or stale notes: +## What ZCP coordinates behind the prompt -- runtime services and managed services, -- runtime layout, such as one app runtime, a dev+stage pair, or a local checkout linked to a Zerops runtime, -- env-var keys and references, -- build/deploy events, runtime logs, and recent verification results, -- current work state when a session resumes after an interruption. +ZCP is intentionally opinionated about the concerns an agent has to resolve during app work. You usually do not name these concerns in the prompt. ZCP provides them through project state, tools, guidance, and workflow instructions so the agent can work from evidence. + +| Concern | What ZCP gives the agent | What that means for you | +| ---------------- | ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| Project state | Live services, env-var keys, Zerops references, recent events, logs, verification output, and saved work state. | You do not have to paste a service inventory or reconstruct what happened. | +| Runtime target | A model for choosing the app runtime that should receive code changes. | The agent can identify where app work belongs before editing or deploying. | +| Managed services | Knowledge and env wiring patterns for database, cache, queue, search, storage, mail, and similar services. | Product intent can imply real dependencies without a manual wiring checklist. | +| Service setup | Project-scoped operations for using existing services or creating missing runtimes and dependencies. | Infrastructure shape can be established before app work starts. | +| App wiring | Zerops-specific rules for `zerops.yaml`, env references, ports, commands, public access, and build/deploy behavior. | The app is wired for Zerops rather than for a generic cloud template. | +| Deploy evidence | Build logs, runtime logs, platform events, service status, HTTP checks, and structured verification output. | A failed deploy becomes a diagnosis surface instead of guesswork. | +| Behavior proof | A done gate based on the requested behavior, not only on a successful build or reachable root URL. | The final answer can point to what was actually checked. | +| Delivery handoff | A workflow contract for direct proof first, then git push, CI, or human handoff when that is the chosen path. | Shipping setup follows a verified running result. | + +The exact labels for layouts, delivery modes, and setup routes live in the [Glossary](/zcp/glossary) and [Workflow terms](/zcp/reference/agent-workflow). + +## Service setup prepares the project shape -This is why a prompt can stay short. The agent can inspect what exists instead of asking you to paste the project inventory. +Before app code work starts, ZCP helps the agent make three decisions visible: -## 2. Fit the request to services and runtimes +- Which runtime service is the app target? +- Which managed services are dependencies? +- Does the existing project shape fit the request? -The agent then decides where the app work belongs. The `zcp` service is the control-plane setup, not the app target. App code deploys to runtime services such as `appdev`, `appstage`, or `app`; managed services such as databases, cache, storage, search, or queues provide dependencies. +If the services already exist, the agent can use them. If a needed runtime or dependency is missing, ZCP can create it through project-scoped operations. If the choice affects cost, product scope, credentials, a destructive action, or which stage/runtime should be used, the workflow tells the agent to stop and ask. -If the right services already exist, the agent should use them. If a requested app needs missing infrastructure, the agent can create it before app work starts. If the choice affects product scope, cost, credentials, or destructive changes, the agent should stop and ask. +Service setup is finished when the app runtime and dependencies are known. It is not the finished app; it is the project shape that lets app work happen in the right place. -The important user-facing distinction is simple: infrastructure setup prepares the project shape, while the development loop proves the app behavior. If the agent starts deploying before it knows the runtime target, or keeps creating services after the app is already running, ask it to read project state and explain the target. +## App work changes code and platform wiring together -## 3. Wire the app to Zerops +After the runtime scope is known, ZCP gives the agent the platform knowledge needed to make the application change. In practice this often spans both source code and Zerops wiring: -App work usually spans code and platform wiring. The agent may need to update application files, `zerops.yaml`, service references, env vars, migrations, seeds, or framework configuration so the app runtime can reach its managed services after deploy. +- app files, +- `zerops.yaml` build and run setup, +- env references to managed services, +- migrations, seeds, and framework config, +- start commands, ports, and public HTTP support, +- local `.env` generation when using local setup. -ZCP guidance matters here because Zerops has its own build, deploy, networking, and env-reference rules. The agent should use those rules instead of importing assumptions from Docker Compose, Kubernetes, or a generic cloud template. +ZCP guidance matters because Zerops is its own platform. Service references, build/deploy behavior, public access, scaling, and env resolution do not follow Docker Compose or Kubernetes conventions. The agent gets Zerops-specific rules instead of relying on a generic cloud template. -## 4. Deploy, read evidence, and retry from the cause +The first functional deploy goes through ZCP's direct deploy path. That deploy creates the running result the agent can verify. A repository push or CI handoff can follow, but it should not replace the first proof. -Deploy is part of the work, not a separate instruction. ZCP gives the agent access to the Zerops deploy path and the evidence needed when it fails: build output, runtime logs, project events, service status, and recovery hints. +## Recovery is evidence-driven -A failed build, broken start command, missing env reference, or unreachable route should lead to evidence-driven fixes. Repeating the same deploy without a new cause is not progress; after repeated failure or a missing decision, the useful result is a blocker. +When something fails, ZCP gives the agent the evidence surface that matches the failure: -## 5. Verify behavior, not only reachability +| Failure shape | Useful evidence | +| ------------------------ | ------------------------------------------------------------------------------------------------------------ | +| Build failed | Build logs, build commands, dependency manifests, deploy file list. | +| Runtime failed to start | Runtime or prepare logs, start command, ports, env references. | +| Route or behavior failed | Verify output, HTTP response, runtime logs at request time, stored state. | +| Network path failed | VPN, SSH, DNS, subdomain readiness, service status, named transport error. | +| Config was rejected | Field-level rejection, `zerops.yaml`, setup name, env reference, service settings. | +| Credential failed | The named credential surface: Zerops token, git token, SSH, managed-service credential, or external API key. | -A successful deploy proves only that the runtime built and started. Platform reachability checks prove the service is running and reachable. They do not prove the product request. +Retrying the same deploy without new evidence is not progress. The workflow pushes the agent toward one of three outcomes: fix from a cause, ask for the missing decision, or report a blocker with the category, evidence read, and attempts made. -For a task-board app, the behavior check is not "the page returns 200". The useful proof is the user-facing workflow the prompt asked for: tasks can be created, edited, moved, persisted, or shared according to the requested scope. For an API task, proof might be a JSON response from the endpoint. For a worker task, proof might be a processed job and the resulting stored state. +## Verification has two layers -## 6. Return proof or a concrete blocker +A successful deploy proves that Zerops accepted the build and started the runtime. Platform reachability checks prove the service is running and reachable. They still do not prove the product request. -A finished ZCP-backed app task should leave one of two things: +For a task-board app, useful behavior proof might be: -- proof: the changed runtime, URL or endpoint, and the specific behavior that was verified, -- blocker: the evidence read, what was tried, and the credential, decision, unsupported fit, or repeated failure that prevents completion. +- create a task, +- move it between columns, +- refresh the page, +- confirm the task is still there. -That final proof is the practical difference between "the agent wrote code" and "the app task is done". +For an API task, proof might be a JSON response from the requested endpoint and stored data behind it. For a worker task, proof might be a processed job and the resulting database or object-storage state. For a staging request, proof belongs on the stage runtime, not only on dev. -## Remote and local setup +The final answer should make that proof inspectable: runtime name, URL or endpoint, behavior checked, and delivery choice if one was set. -The same `zcp` binary can run in two places. The project loop stays the same; the work surface changes. +## Delivery happens after proof -| Path | What runs where | Practical effect | -|---|---|---| -| Remote setup | A `zcp@1` service runs the `zcp` binary inside the Zerops project. **Include Coding Agent** adds the bundled agent CLI; **Cloud IDE** adds browser VS Code. | Work happens inside the project boundary. The agent can use project-private networking and runtime file mounts. | -| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your local editor or CLI agent talks to it. | App edits, dev server, deploy source, and git credentials stay local. Managed services are reached over Zerops VPN, and `.env` generation bridges project credentials into your local app. | +Delivery choice controls how future changes ship after a verified runtime exists: + +| Choice | Use when | +| ------------------ | ------------------------------------------------------------------------------ | +| Keep direct deploy | Early development, demos, dev/stage iteration, or agent-owned runtime changes. | +| Push to git | You want commits, review, repository history, or a repo-triggered build. | +| External handoff | CI, release management, or a human owns the next deploy. | + +Packaging a running service is a separate advanced reuse path. It turns a deployed runtime into a re-importable bundle for another Zerops project. It is useful for handoff or reuse, not for deploying the next app change; see [Package a running service](/zcp/workflows/package-running-service). + +## Remote and local setup use the same loop + +The same `zcp` binary can run in two places. The control loop stays the same; the filesystem and network path change. + +| Path | What runs where | Practical effect | +| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Remote setup | A `zcp@1` service runs the `zcp` binary inside the Zerops project. **Include Coding Agent** adds the bundled agent CLI; **Cloud IDE** adds browser VS Code. | Work happens inside the project boundary. The agent can use project-private networking and runtime file mounts. | +| Local setup | The `zcp` binary runs on your laptop after `zcp init`, and your local editor or CLI agent talks to it. | App files, local data, deploy source, and git credentials stay on your machine. Managed services are reached over Zerops VPN, and `.env` generation bridges project credentials into your local app. | Choose the setup based on where you want the agent and filesystem to live: [Choose remote or local setup](/zcp/setup/choose-workspace). +## Signs of a healthy ZCP run + +A well-shaped run should: + +- name the runtime scope before editing or deploying, +- use existing services when they fit, +- create missing services only before app work starts, +- read logs, events, and verify output when failure occurs, +- distinguish runtime reachability from requested behavior, +- stop before destructive actions, ambiguous runtime/stage choices, or missing credentials, +- end with proof or a concrete blocker. + +That is the practical difference between "the agent wrote code" and "the app task is done". + ## Where to go deeper - [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. -- [Workflow terms](/zcp/reference/agent-workflow) - exact runtime layouts, delivery terms, and completion evidence. +- [Workflow terms](/zcp/reference/agent-workflow) - exact runtime layouts, delivery terms, failure categories, and completion evidence. - [Troubleshooting](/zcp/reference/troubleshooting) - practical recovery when a run gets stuck. diff --git a/apps/docs/content/zcp/glossary.mdx b/apps/docs/content/zcp/glossary.mdx index ef24d32f6..579a4c702 100644 --- a/apps/docs/content/zcp/glossary.mdx +++ b/apps/docs/content/zcp/glossary.mdx @@ -1,15 +1,25 @@ --- -title: "Glossary" -description: "Short definitions for ZCP terms used across the docs." +title: 'Glossary' +description: 'Short definitions for ZCP terms, exact workflow labels, setup names, delivery modes, and credential names.' --- +Use the exact labels here when you are reading ZCP status, writing agent policy, debugging a session, or reviewing a handoff. In normal prompts, describe the outcome you want. + ## Core names **ZCP** - Zerops Control Plane for coding agents: the project-scoped control plane documented in this section. **ZCP operations** - the project-scoped operations surface exposed to a coding agent. In MCP clients, this appears as the ZCP MCP server. -**`zcp` binary** - the executable. Runs inside remote setup or on your laptop in local setup. +**MCP server** - the Model Context Protocol server that exposes ZCP operations to an agent client. + +**Agent client** - the editor, CLI, or hosted agent runtime that connects to ZCP operations. + +**`zcp` binary** - the executable. It runs inside remote setup or on your laptop in local setup. + +**`zcp` service** - the service instance in a Zerops project that hosts remote setup. + +**`zcp@1` service** - the Zerops service type behind remote setup. **zCLI** - the Zerops command-line client for humans, scripts, VPN, and CI. It coexists with ZCP. @@ -19,54 +29,128 @@ description: "Short definitions for ZCP terms used across the docs." **Remote setup** - ZCP running inside a Zerops `zcp@1` service in the project. -**`zcp@1` service** - the Zerops service type behind remote setup. - -**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI and preconfigures it to use the project's ZCP operations. +**Include Coding Agent** - the remote setup option that adds the currently supported bundled agent CLI, currently Claude Code, and preconfigures it to use the project's ZCP operations. **Cloud IDE** - browser-based VS Code served by remote setup. +**Browser VS Code** - the dashboard entry point into the Cloud IDE. + **AI Agent environment** - a recipe environment preset that creates app services plus remote setup with **Include Coding Agent** enabled. It is a recipe preset, not a separate ZCP setup path. **Local setup** - the `zcp` binary running on your laptop after `zcp init`, while your editor or CLI agent talks to it. **Local agent bridge** - local setup with the `zcp` binary connected to a local agent client. -## Workflows and results +**Env bridge** - local setup behavior that writes a local `.env` snapshot from project env vars and Zerops references so local app code can reach managed services over VPN. + +## Work phases and loops **Service setup** - the infrastructure phase behind an app prompt. It uses existing runtime and managed services when they fit, creates missing services when needed, then stops before app code, `zerops.yaml`, or deploy. -**Deploy, verify, and fix** - the app phase. It scopes the runtime, changes code/config, deploys, verifies reachability, verifies requested behavior, and fixes failures. +**Infrastructure setup** - user-facing synonym for service setup. + +**Setup route** - the exact label for how service setup starts: + +- `adopt` - runtime services already exist and should be used. +- `recipe` - an empty or ZCP-only project matches a known recipe/stack. +- `classic` - an empty project needs a custom service plan. +- `resume` - an interrupted setup should continue from saved state and live services. + +**Deploy, verify, and fix** - the app phase. It scopes the runtime, changes code/config, deploys, verifies reachability, verifies requested behavior, and fixes failures from evidence. + +**Develop flow** - exact reference name for the deploy, verify, and fix loop. **Runtime scope** - the app runtime the change targets, such as `appdev`, `appstage`, `app`, or a linked local target. -**Delivery choice** - what happens to future changes after a verified deploy: direct deploy, git-push, or external handoff. +**Direct deploy** - a ZCP deploy from the current source to the scoped runtime. The first verified running result uses direct deploy before delivery setup is applied. + +**Reachability verification** - checks that the runtime exists, is running, has no recent blocking errors, and can answer an HTTP probe when it is an HTTP service. + +**Behavior verification** - checks that the user-requested app behavior works on the real URL, endpoint, worker result, or stored state. + +**Completion evidence** - the runtime, deploy, reachability check, behavior check, URL/endpoint/UI/state proof, and delivery choice or blocker that justify calling the task done. + +**Proof** - user-inspectable completion evidence, such as a URL, endpoint result, UI state, processed job, or stored result. **ZCP status** - a live project read the agent can use after interruption or confusion. Ask for it before letting the agent change anything else. **Blocker** - a clear stop state with evidence: failure category, runtime in scope, what was tried, and what decision or credential is needed. +## Delivery and reuse + +**Delivery choice** - what happens to future changes after a verified deploy: direct deploy, git-push, or external handoff. + +**Delivery mode** - exact reference label for delivery choice: + +- `auto` - keep direct deploy for future changes. +- `git-push` - commit and push to a configured remote, then observe/verify any tracked build. +- `manual` - external CI, release process, or a human owns future delivery. + +**Git-push capability** - whether ZCP has enough remote URL and credential setup to push from remote setup. It can exist even when the current delivery mode is `auto`. + +**Build integration** - repository-triggered build/deploy path that ZCP may configure or observe, such as a Zerops dashboard webhook or GitHub Actions. It is separate from git-push capability and delivery mode. + +**External handoff** - delivery mode where ZCP records proof and state, but a human or external system owns future deploys. + +**Package a running service** - advanced reuse workflow that turns one deployed runtime and its managed dependencies into a re-importable bundle. It is not the normal path for deploying the next app change. + +**Packaging env bucket** - exact labels used when classifying project env vars during packaging: + +- `infrastructure` - value comes from a managed-service reference and should be regenerated by the new project. +- `auto-secret` - local app secret that should be freshly generated on re-import. +- `external-secret` - third-party API key or credential that becomes a `REPLACE_ME` placeholder. +- `plain-config` - literal non-secret config copied into the bundle. + ## Project and runtime terms **Runtime service** - a service that runs app code. -**Managed service** - database, cache, queue, search, storage, or similar dependency. It provides connection details; it is not an app deploy target. +**Target runtime service** - the specific runtime service selected for the current app change. + +**Managed service** - database, cache, queue, search, storage, mail, or similar dependency. It provides connection details; it is not an app deploy target. **Runtime layout** - which app runtime services ZCP should use: - `standard` - dev runtime plus explicit stage runtime. - `dev` - one mutable development runtime. - `simple` - one runtime with no dev/stage split. -- `local-stage` - local checkout linked to one Zerops runtime as deploy target. -- `local-only` - local checkout with no linked runtime yet. +- `local-stage` - local source directory linked to one Zerops runtime as deploy target. +- `local-only` - local source directory with no linked runtime yet. + +**Dev runtime** - mutable runtime used for iterative development. + +**Stage runtime** - review or promotion target. Stage is not production. **Service scaling mode** - Zerops scaling setting such as `HA` or `NON_HA`. Different from runtime layout. -**Stage** - review or promotion target. Stage is not production. +**Public subdomain access** - Zerops public URL access for an eligible HTTP runtime. Workers and non-HTTP services do not get useful public URLs. + +## Failures and recovery + +**Failure category** - exact label that points to the first useful evidence surface: + +- `build` - build phase failed. +- `start` - build passed, but runtime start or prepare failed. +- `verify` - runtime exists, but reachability or behavior failed. +- `network` - transport, DNS, VPN, SSH, or service-to-service reach failed. +- `config` - `zerops.yaml`, env vars, setup block, or service settings mismatch. +- `credential` - Zerops, git, SSH, managed-service, or external API credential failed. +- `other` - no known category matched. + +**Recovery hint** - structured next move surfaced by ZCP when the failure has an actionable recovery path. + +**Confirmation gate** - an operation that pauses until the user explicitly confirms the named target or consequence. + +**Destructive import override** - import action that would replace an existing service stack. ZCP refuses first, names the affected services, and requires a matching acknowledgment before proceeding. ## Credentials +**Project-scoped token** - Zerops token that can access exactly one project. ZCP expects this scope. + **`ZCP_API_KEY`** - project-scoped Zerops token used by ZCP. **`GIT_TOKEN`** - git provider credential used by remote git-push delivery when needed. **`ZEROPS_TOKEN`** - Zerops token commonly used by GitHub Actions or external CI that runs `zcli`. + +**External secret** - third-party credential such as Stripe, OpenAI, Mailgun, or GitHub API access. ZCP can wire placeholders and env vars, but the secret itself remains your responsibility. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index 078d37d45..57c7bc082 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -1,6 +1,6 @@ --- -title: "Zerops Control Plane for coding agents" -description: "Why ZCP changes how a coding agent works with a Zerops project, and what you can leave out of the prompt." +title: 'Zerops Control Plane for coding agents' +description: 'Why ZCP changes how a coding agent works with a Zerops project, and what you can leave out of the prompt.' --- import Image from '/src/components/Image'; @@ -55,7 +55,7 @@ To start using ZCP, choose one path: add remote setup to a Zerops project, or in **Zerops token.** ZCP itself connects to Zerops through a project-scoped Zerops token. In remote setup, Zerops injects that token into the `zcp@1` service. In local setup, you provide a project-scoped token when you initialize ZCP beside your editor or CLI agent. Token details live in [Tokens and credentials](/zcp/security/tokens-and-project-access). -**Workspace freedom.** The `zcp@1` service is still a normal project service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and shape the workspace around the bundled setup. ZCP is the project control plane available to the agent; it is not a closed agent product. Details live in [Set up remote ZCP](/zcp/setup/hosted-workspace#bring-your-own-agent-or-tools). +**Workspace freedom.** The `zcp@1` service is still a normal project service. You can install another agent CLI, add private MCP servers or helper tools, edit `CLAUDE.md`, add team dotfiles, and shape the workspace around the bundled setup. ZCP is the project control plane available to the agent; it is not a closed agent product. Details live in [Use remote ZCP workspace](/zcp/setup/hosted-workspace#make-customization-persistent). :::caution Production boundary Use ZCP in development or staging projects. Production should stay in a separate Zerops project and receive promoted work through your CI or release process; see [Production boundary](/zcp/security/production-policy). @@ -65,24 +65,24 @@ Use ZCP in development or staging projects. Production should stay in a separate First-read path: -| If you want to | Read | -|---|---| -| Try the shortest first run | [Quickstart](/zcp/quickstart) | -| Understand what happens behind the prompt | [How ZCP works](/zcp/concept/how-it-works) | -| Choose remote or local setup | [Choose remote or local setup](/zcp/setup/choose-workspace) | -| Start building after setup | [Build with ZCP](/zcp/workflows/build-with-zcp) | +| If you want to | Read | +| ----------------------------------------- | ----------------------------------------------------------- | +| Try the shortest first run | [Quickstart](/zcp/quickstart) | +| Understand what happens behind the prompt | [How ZCP works](/zcp/concept/how-it-works) | +| Choose remote or local setup | [Choose remote or local setup](/zcp/setup/choose-workspace) | +| Start building after setup | [Build with ZCP](/zcp/workflows/build-with-zcp) | Specific tasks and reference: -| If you want to | Read | -|---|---| -| Add remote setup to a project | [Set up remote ZCP](/zcp/setup/hosted-workspace) | -| Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | -| Decide how finished work ships | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | -| Understand tokens and project boundaries | [Trust model](/zcp/security/trust-model) | -| Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | -| Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | -| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | +| If you want to | Read | +| ---------------------------------------- | ----------------------------------------------------------------- | +| Use remote workspace in a project | [Use remote ZCP workspace](/zcp/setup/hosted-workspace) | +| Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | +| Decide how finished work ships | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | +| Understand tokens and project boundaries | [Trust model](/zcp/security/trust-model) | +| Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | +| Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | +| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | ## What stays outside ZCP diff --git a/apps/docs/content/zcp/reference/agent-workflow.mdx b/apps/docs/content/zcp/reference/agent-workflow.mdx index ac6417212..68dec8296 100644 --- a/apps/docs/content/zcp/reference/agent-workflow.mdx +++ b/apps/docs/content/zcp/reference/agent-workflow.mdx @@ -1,6 +1,6 @@ --- -title: "Workflow terms" -description: "Precise ZCP workflow terms: session layers, service setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence." +title: 'Workflow terms' +description: 'Precise ZCP workflow terms: session layers, service setup routes, develop flow, runtime layouts, delivery modes, failure categories, and completion evidence.' --- Exact vocabulary for ZCP workflows. Day-to-day prompts should describe outcomes; reference, audits, policies, and handoffs can use these names when precision matters. @@ -9,11 +9,11 @@ Exact vocabulary for ZCP workflows. Day-to-day prompts should describe outcomes; Most workflow mistakes come from confusing these layers: -| Layer | What it is | What changes here | -|---|---|---| -| ZCP setup | Where ZCP runs: remote `zcp@1` service or local `zcp` binary. | No app code should be deployed to the setup itself. | -| Target runtime service | The app runtime: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys. | -| Managed services | Databases, caches, queues, search, storage, mail. | Schema/data operations and credentials, never app code deploys. | +| Layer | What it is | What changes here | +| ---------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------- | +| ZCP setup | Where ZCP runs: remote `zcp@1` service or local `zcp` binary. | No app code should be deployed to the setup itself. | +| Target runtime service | The app runtime: `appdev`, `appstage`, `app`, or a linked local deploy target. | App files, `zerops.yaml`, deploys. | +| Managed services | Databases, caches, queues, search, storage, mail. | Schema/data operations and credentials, never app code deploys. | The `zcp` service is the control surface, not the app runtime. @@ -40,12 +40,12 @@ flowchart TD known -- no --> classic ``` -| Route | Use when | Wrong signal | -|---|---|---| -| `adopt` | Runtime services already exist, including recipe-created projects. | Recreating services that exist, or targeting `zcp` as the app. | -| `recipe` | The project is empty or only has ZCP, and the request matches a known stack recipe. | Deploying an unchanged starter as if it were the requested product. | -| `classic` | The project is empty and needs a custom service plan. | Writing app code before service ownership and runtime scope are known. | -| `resume` | A previous setup was interrupted. | Starting from scratch without acknowledging live services and saved state. | +| Route | Use when | Wrong signal | +| --------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | +| `adopt` | Runtime services already exist, including recipe-created projects. | Recreating services that exist, or targeting `zcp` as the app. | +| `recipe` | The project is empty or only has ZCP, and the request matches a known stack recipe. | Deploying an unchanged starter as if it were the requested product. | +| `classic` | The project is empty and needs a custom service plan. | Writing app code before service ownership and runtime scope are known. | +| `resume` | A previous setup was interrupted. | Starting from scratch without acknowledging live services and saved state. | Service setup stops when app runtimes and managed dependencies are known. App code, `zerops.yaml`, and the first deploy belong to the develop flow. @@ -85,13 +85,13 @@ Dynamic dev runtimes may need an explicit start or restart after deploy. Built-i Runtime layout describes which app runtime services ZCP should use. Zerops service scaling mode, such as `HA` or `NON_HA`, is a separate service setting. -| Layout | Meaning | Typical names | -|---|---|---| -| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage is explicit. | `appdev` + `appstage` | -| `dev` | One mutable development runtime. | `appdev` | -| `simple` | One runtime with no dev/stage split. | `app` | -| `local-stage` | Local checkout linked to one Zerops runtime for deploys. | local checkout + `appstage` | -| `local-only` | Local checkout with no linked deploy target yet. | local checkout | +| Layout | Meaning | Typical names | +| ------------- | ---------------------------------------------------------------------------------------------- | ---------------------------- | +| `standard` | Separate dev and stage runtime services. Development usually starts on dev; stage is explicit. | `appdev` + `appstage` | +| `dev` | One mutable development runtime. | `appdev` | +| `simple` | One runtime with no dev/stage split. | `app` | +| `local-stage` | Local source directory linked to one Zerops runtime for deploys. | local directory + `appstage` | +| `local-only` | Local source directory with no linked deploy target yet. | local directory | The stage hostname is supplied by project state. ZCP should not invent it from the dev hostname. @@ -99,25 +99,25 @@ The stage hostname is supplied by project state. ZCP should not invent it from t Delivery mode applies after a verified deploy and describes how future changes ship. It does not redirect the first successful deploy. -| Exact mode | User-facing choice | Meaning | -|---|---|---| -| `auto` | Keep direct deploy | ZCP keeps deploying future changes directly to the target runtime. | -| `git-push` | Push to git | The agent commits and pushes to a configured remote; any resulting build still needs observation and verification. | -| `manual` | External handoff | CI, release process, or a human owns future delivery; ZCP records evidence but does not initiate the next deploy. | +| Exact mode | User-facing choice | Meaning | +| ---------- | ------------------ | ------------------------------------------------------------------------------------------------------------------ | +| `auto` | Keep direct deploy | ZCP keeps deploying future changes directly to the target runtime. | +| `git-push` | Push to git | The agent commits and pushes to a configured remote; any resulting build still needs observation and verification. | +| `manual` | External handoff | CI, release process, or a human owns future delivery; ZCP records evidence but does not initiate the next deploy. | Git-push capability, delivery mode, and build integration are separate. A project can have git-push configured while still using direct deploy for a given session. ## Failure categories -| Category | Meaning | Read first | -|---|---|---| -| `build` | Build phase failed. | Build logs and build commands. | -| `start` | Build passed, runtime failed to start or crashed. | Prepare/runtime logs and start command. | -| `verify` | Runtime exists but reachability or behavior failed. | Failing check detail and runtime logs at request time. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and network path. | -| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Structured rejection detail and config files. | -| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | -| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | +| Category | Meaning | Read first | +| ------------ | --------------------------------------------------------------------- | ------------------------------------------------------ | +| `build` | Build phase failed. | Build logs and build commands. | +| `start` | Build passed, runtime failed to start or crashed. | Prepare/runtime logs and start command. | +| `verify` | Runtime exists but reachability or behavior failed. | Failing check detail and runtime logs at request time. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | Transport error and network path. | +| `config` | `zerops.yaml`, env vars, setup block, or service settings mismatch. | Structured rejection detail and config files. | +| `credential` | Zerops, git, SSH, managed-service, or external API credential failed. | The named credential surface. | +| `other` | No known category matched. | Raw event/log evidence; stop if repeated. | Categorization is what turns retries into evidence-driven fixes. Recovery moves and symptom mapping live in [Troubleshooting](/zcp/reference/troubleshooting). @@ -150,12 +150,12 @@ A green build with a broken route is not completion. A clear blocker is acceptab A session is well-shaped if platform evidence and the agent report answer: -| Question | Evidence | -|---|---| -| Which setup route and runtime layout were used? | ZCP status, service metadata, project services. | -| Where did service setup end and app work begin? | Work-session state, deploy history, file changes. | -| Which runtime was deployed and verified? | Service events, deploy result, verify output. | -| What behavior proved success? | Endpoint/UI/job/data proof in the final report. | -| What controls future delivery? | Delivery mode, git-push state, build integration, or manual handoff note. | +| Question | Evidence | +| ----------------------------------------------- | ------------------------------------------------------------------------- | +| Which setup route and runtime layout were used? | ZCP status, service metadata, project services. | +| Where did service setup end and app work begin? | Work-session state, deploy history, file changes. | +| Which runtime was deployed and verified? | Service events, deploy result, verify output. | +| What behavior proved success? | Endpoint/UI/job/data proof in the final report. | +| What controls future delivery? | Delivery mode, git-push state, build integration, or manual handoff note. | Operation-name reference lives in [Advanced operations](/zcp/reference/mcp-operations). Recovery shortcuts live in [Troubleshooting](/zcp/reference/troubleshooting). diff --git a/apps/docs/content/zcp/security/production-policy.mdx b/apps/docs/content/zcp/security/production-policy.mdx index 07f2d2c64..6b19576b4 100644 --- a/apps/docs/content/zcp/security/production-policy.mdx +++ b/apps/docs/content/zcp/security/production-policy.mdx @@ -1,60 +1,93 @@ --- -title: "Production boundary" +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." --- -Rule: keep ZCP in a development or staging Zerops project. Production should be a separate project without a `zcp` service; promotion happens through CI or a release pipeline with production-scoped credentials. +Rule: use ZCP in development or staging projects. Production should be a separate Zerops project without a `zcp` service, and production deploys should come from CI, a release pipeline, or a deliberate human `zcli` push using production-scoped credentials. -The platform doesn't stop you from adding a `zcp` service to a production project. Treat the dev/staging boundary as load-bearing because crossing it puts an agent inside production, not because Zerops will refuse the configuration. +Zerops does not prevent you from adding a `zcp` service to production. The policy exists because ZCP gives a coding agent project-scoped operational access. In production, that is the wrong blast radius for normal app development. -## Why a separate production project +## Recommended project layout -The clean separation is the project boundary: ZCP runs where the agent works, while production runs in its own project. +| Project | Contains | Who operates it | +| ----------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------ | +| Development / staging project | `zcp` service, agent workspace, dev runtime, optional stage runtime, non-production managed services | Agent plus humans | +| Production project | Production runtimes, production managed services, production env values, production backups and scaling | CI/release process plus humans | -- **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 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. +The two projects use different Zerops tokens. A token scoped to the development project cannot reach production. A production token should not be placed in the development `zcp` service or a local `.mcp.json` used by the agent. -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 with no `zcp` service, where deploys come from your CI or release pipeline. +## Why production is a separate project -## Stage as proof +- **The project is the security boundary.** ZCP binds to one project at startup. Keeping production in another project prevents a development agent from touching production by accident. +- **Secrets stay clean.** Production env values, database credentials, object-storage keys, and third-party secrets stay in the production project. +- **Operational policies can differ.** Production often needs HA services, stronger backup retention, stricter scaling, alerts, and release approvals. Development can stay cheaper and more flexible. +- **Audit trails stay readable.** Development experiments and agent retries do not mix with production deploy evidence. -Stage isn't a formality. It's where the agent proves the change works on the same architecture pattern production uses — same runtime version, same managed service types, same build/deploy path — before promotion to the separate production project. +This separation matters even when the same source repository deploys to both projects. -Use stage as the production-like rehearsal: +## What stage proves -- The agent develops on dev (`appdev` in the `standard` runtime layout). -- 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. +Stage is the production-like rehearsal inside the development/staging project. It is where the agent proves the change before promotion leaves the ZCP project. -Skip stage and you skip the layer where production-like failures show up. +Use stage to match production where it matters: + +- same runtime family and version, +- same managed service types, +- same build and start command pattern, +- same deploy path, +- behavior checks against a real running service. + +Stage is not production. It should use non-production data and non-production secrets. A green stage means the change is ready for the production release path, not that the agent should deploy to production itself. ## 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: +After stage verifies, ZCP's job is done for that change. Promotion to production happens outside the agent loop: -- 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). +- CI deploys a merged commit or release tag to the production project. +- A release pipeline runs `zcli push` with a production-scoped `ZEROPS_TOKEN`. +- A human runs `zcli push` against the production project with production credentials. -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 can prepare the handoff by pushing code, summarizing verification evidence, and naming the runtime and URL it verified. It should not bridge the development project to the production project. Broader CI/CD reference: [CI/CD with Zerops](/guides/ci-cd). +## Credential rules + +| Credential | Production policy | +| ----------------------------------- | -------------------------------------------------------------------------------------------------------- | +| Development `ZCP_API_KEY` | Never grants production access. Keep it scoped to the development/staging project. | +| Production `ZEROPS_TOKEN` | Store only in the production CI/release secret store. Do not place it in the development `zcp` service. | +| Agent subscription or model API key | May be used by the agent, but it does not grant Zerops production access by itself. | +| Git credentials | May push source changes, but production deploy authority should still be controlled by the release path. | + +If a production deploy fails, investigate in the production project with production logs, events, backups, and CI output. Do not attach the development agent directly to production as a shortcut. + ## What not to do -| Anti-pattern | Why it's wrong | -|---|---| -| Add a `zcp` service to your production project | The agent gets project-scoped operational access to 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-like 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 ZCP setup | +| Anti-pattern | Why it breaks the boundary | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | +| Add a `zcp` service to the production project | Puts a coding agent inside production with project-scoped operational access. | +| Put a production token in local `.mcp.json` | Lets the local agent operate production from your machine. | +| Store production `ZEROPS_TOKEN` in the development `zcp` service | Lets development tooling deploy to production outside the release path. | +| Treat dev verification as production approval | Dev can use hot-reload, mutable data, or cheaper services. Stage is the rehearsal; production approval is separate. | +| Let the agent deploy directly from a development session into production | Bypasses the release evidence and credential boundary. | + +## Acceptable agent involvement + +The agent can still help before production promotion: + +- make the code change in development, +- deploy and verify dev/stage runtimes, +- produce the URL, endpoint result, or UI proof it verified, +- push or prepare the code path your team uses for review, +- summarize release notes and known blockers for the human or CI handoff. + +Stop there. Production execution belongs to the production release path. ## 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. -- [Troubleshooting](/zcp/reference/troubleshooting) — recover or take over before promoting. -- [Backup](/features/backup) — the production data safety net that operates outside ZCP. -- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) — the dev/stage loop this policy assumes. +- [Trust model](/zcp/security/trust-model) - the project boundary that makes this policy enforceable. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - project-scoped token handling. +- [Choose remote or local setup](/zcp/setup/choose-workspace) - pick the development setup path. +- [Build with ZCP](/zcp/workflows/build-with-zcp) - the dev/stage loop this policy assumes. +- [Backup](/features/backup) - production recovery outside ZCP. 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 ecc7d79d6..d7899d80e 100644 --- a/apps/docs/content/zcp/security/tokens-and-project-access.mdx +++ b/apps/docs/content/zcp/security/tokens-and-project-access.mdx @@ -1,58 +1,72 @@ --- -title: "Tokens and credentials" -description: "Project-scoped Zerops tokens, git credentials, and the confirmation gates ZCP enforces before destructive actions." +title: 'Tokens and credentials' +description: 'Project-scoped Zerops tokens, git credentials, and the confirmation gates ZCP enforces before destructive actions.' --- -ZCP enforces the project boundary at three layers: token shape (one project, exactly), where the token lives (platform-injected for remote setup, in `.mcp.json` for local setup), and confirmation gates on operations that are not reversible from inside the conversation. +ZCP uses a Zerops API token to operate exactly one project. The important rule is simple: `ZCP_API_KEY` is a project credential for ZCP, not an agent login, not a git token, and not a general account token. -## Recommended token shape — one project, full access +Remote setup gets `ZCP_API_KEY` from the Zerops platform. Local setup reads it from `.mcp.json`. In both paths, ZCP validates the token at startup and refuses tokens that resolve to no project or multiple projects. -ZCP requires a Zerops API token that resolves to **exactly one project** at startup. For normal agent workflows, use **Custom access per project** scoped to a single project with **Full access**. Read-only tokens can pass startup, but they fail as soon as the agent tries to deploy, write env vars, or operate services. +## Credential map -To generate one: +| Name | What it authorizes | Where it belongs | +| ---------------------------- | -------------------------------------------- | --------------------------------------------------------------------------- | +| `ZCP_API_KEY` | ZCP against one Zerops project | Remote: `zcp` service env injected by Zerops. Local: `.mcp.json` env block. | +| Agent login or model API key | The coding agent itself | The bundled agent in remote setup, or your local agent client. | +| `GIT_TOKEN` | Git push from remote setup to a git provider | Secret env var on the `zcp` service when remote git-push delivery needs it. | +| `ZEROPS_TOKEN` | `zcli` in GitHub Actions or external CI | Repository or CI secret for the workflow that deploys to Zerops. | -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. +Keeping these names separate prevents most setup and delivery failures. -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. +## Recommended `ZCP_API_KEY` shape -## Rejected token shapes +Use a Zerops API token with **Custom access per project**, scoped to exactly one project, with **Full access** for normal agent work. + +Read-only tokens can authenticate, but they fail as soon as the agent needs to deploy, write env vars, restart services, scale, or change public access. Account-wide or multi-project tokens are refused before the agent can operate. + +To generate the token: + +1. Open [Settings -> Access Tokens Management](https://app.zerops.io/settings/token-management). +2. Create a token and name it for the project, for example `zcp-`. +3. Choose **Custom access per project**. +4. Add exactly one project. +5. Set that project to **Full access** for normal ZCP work. +6. Create the token and copy the value. Zerops shows it only at creation time. -ZCP validates the token at startup and refuses wrong shapes before the agent can operate the project. +The token's blast radius equals the project and its granted permissions. Other projects, organization settings, and billing stay out of reach. Zerops [Roles & Permissions](/features/rbac#integration-tokens) remain the platform authority. -| Token shape | What ZCP does | Why | -|---|---|---| -| Multi-project, including account-wide full access | Refuses to start | One ZCP process equals one project; ZCP won't pick which one. | -| No project access | Refuses to start | 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 to start | The token no longer authenticates against the Zerops API. | +## Rejected token shapes + +ZCP validates token shape at startup. -The fix is always the same: generate a per-project token and replace `ZCP_API_KEY` on the surface ZCP reads from. In remote setup, that value is injected into the `zcp` service environment. In local setup, it is the `env` block in `.mcp.json`. +| Token shape | What happens | Fix | +| ----------------------------------- | ------------------------------------------- | -------------------------------------------------------------------- | +| Account-wide or multi-project token | ZCP refuses to start. | Generate a token scoped to exactly one project. | +| Token with no project access | ZCP refuses to start. | Grant one project or create a new project-scoped token. | +| Expired or revoked token | ZCP refuses to start or receives HTTP 401. | Replace the token and restart the agent or ZCP process. | +| Read-only project token | Startup may pass, but mutations fail later. | Use full access for normal agent work, or expect read-only behavior. | -Common startup/auth messages: +Common messages: -| Message | Meaning | -|---|---| -| `Token accesses N projects; use project-scoped token` | The token can see more than one project. Replace it with a single-project token. | -| `Token has no project access` | The token authenticates but reaches no project. Grant project access or replace it. | -| `No authentication found: set ZCP_API_KEY or log in with zcli` | No usable token reached the `zcp` process. | -| `AUTH_TOKEN_EXPIRED` or HTTP 401 | The token expired, was revoked, or is otherwise invalid. Replace it and restart the agent. | +| Message | Meaning | +| -------------------------------------------------------------- | ------------------------------------------------- | +| `Token accesses N projects; use project-scoped token` | The token can see more than one project. | +| `Token has no project access` | The token authenticates but reaches no project. | +| `No authentication found: set ZCP_API_KEY or log in with zcli` | The `zcp` process did not receive a usable token. | +| `AUTH_TOKEN_EXPIRED` or HTTP 401 | The token expired, was revoked, or is invalid. | ## Where the token lives - remote vs local {#where-the-token-lives--remote-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: +ZCP reads `ZCP_API_KEY` from its process environment at startup. It does not write the token somewhere else or exchange it for a derived credential. -| Setup | Where `ZCP_API_KEY` comes from | Who provisions it | -|---|---|---| -| Remote setup | The `zcp` service container env | Zerops platform - injected automatically when the service boots | -| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You - added manually after `zcp init` | +| Setup | Where `ZCP_API_KEY` comes from | Who provisions it | +| -------------------------------------------- | ------------------------------------------------------- | -------------------------------------------------------- | +| [Remote setup](/zcp/setup/hosted-workspace) | The `zcp` service environment | Zerops injects it automatically when the service starts. | +| [Local setup](/zcp/setup/local-agent-bridge) | The `env` block of `.mcp.json` in the project directory | You add it after `zcp init`. | -In remote setup, the service starts with `ZCP_API_KEY` populated. If **Include Coding Agent** is enabled, the bundled agent uses that environment automatically. +In remote setup, do not hand-edit `ZCP_API_KEY`. Replace or rotate it through the Zerops-managed surface so the service keeps the intended project boundary. -In local setup, `zcp init` writes a token-less `.mcp.json` template; you add the `env` block: +In local setup, `zcp init` writes a token-less `.mcp.json`. Add the token manually: ```json { @@ -68,21 +82,19 @@ In local setup, `zcp init` writes a token-less `.mcp.json` template; you add the } ``` -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. +Add `.mcp.json` to `.gitignore`. Each project directory should have its own file and its own project token. Switching projects means switching directories, not editing one shared credential. -## Git credentials — separate from `ZCP_API_KEY` +## Agent credentials are separate -Three names, three jobs — keeping them straight prevents most credential confusion in [delivery handoff](/zcp/workflows/delivery-handoff): +The bundled agent in remote setup may ask you to sign in or provide a model API key. That is not `ZCP_API_KEY`. -| Name | What it authorizes | Where it lives | -|---|---|---| -| `ZCP_API_KEY` | ZCP itself, against the Zerops API | Container env (remote) or `.mcp.json` env block (local) | -| `GIT_TOKEN` | A git push from remote setup to a remote (GitHub, GitLab) | Remote: secret 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 | +Zerops wires the agent to the project through ZCP. It does not provide your model subscription, store your agent login, or rotate your agent provider credentials. Treat the agent account exactly as you would outside Zerops. -`GIT_TOKEN` only matters when delivery uses git-push and remote setup is the one pushing. In local setup, 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 `zcp` service env when you run git-push setup, ZCP reuses it instead of asking for a new credential. +## Git and CI credentials -`ZEROPS_TOKEN` is a **Zerops API token, not a GitHub token** — an 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. Delivery setup should place it in the CI secret store for the repository that runs the deploy. +`GIT_TOKEN` matters only when remote setup pushes to a git remote. It authorizes git provider access from the `zcp` service. In local setup, your local git CLI uses your normal SSH key or credential helper, so ZCP does not need `GIT_TOKEN`. + +`ZEROPS_TOKEN` is a Zerops API token used by GitHub Actions or another CI system when that system runs `zcli` against Zerops. It is not a GitHub token. It can be the same project-scoped token used as `ZCP_API_KEY`, or a separate project-scoped token if your team prefers separate rotation. With GitHub CLI, the secret shape is: @@ -90,58 +102,57 @@ With GitHub CLI, the secret shape is: gh secret set ZEROPS_TOKEN -b "$ZCP_API_KEY" ``` -Use the GitHub UI instead if your team does not allow local CLI secret writes. +Use the GitHub UI or your CI secret manager instead if your team does not allow local CLI secret writes. ## Rotation -Rotate in the Zerops dashboard, then propagate to the consuming surface: +Rotate in the Zerops dashboard, then update the surface that consumes the token. -- **Remote setup** — the service env value updates; ZCP picks up the new value the next time the `zcp` service boots or restarts. -- **Local setup** — paste the new token into the `env` block of `.mcp.json`, then restart your agent client 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. +| Surface | Rotation step | +| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| Remote `ZCP_API_KEY` | Reconfigure or redeploy remote ZCP through the dashboard-managed path, then restart the `zcp` service so the process gets the new value. | +| Local `ZCP_API_KEY` | Paste the new token into `.mcp.json`, then restart the local agent client. | +| `ZEROPS_TOKEN` in CI | Replace the repository or CI secret. The next workflow run uses the new value. | +| `GIT_TOKEN` | Replace the git-provider credential stored for remote setup. | -Rotations on `ZCP_API_KEY` and `ZEROPS_TOKEN` are independent unless you reused the same Zerops token for both. +Rotation is picked up on next process start or workflow run, not in the middle of a live agent session. ## 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. | Requires explicit approval in the same conversation, including the service name. Remote setup also blocks deleting the `zcp` service it is running in. | -| **Wholesale service replacement** from an import on a service with prior failed deploy history | First returns a summary of what would be replaced and refuses to continue. The agent must show that evidence before asking you to confirm. | +A valid token does not remove every guardrail. ZCP adds explicit confirmation for operations where the loss is not safely reversible from inside the conversation. -ZCP itself adds confirmation only for the two operations above. Your agent client or team policy may still prompt before tool calls. Deploys, env changes, lifecycle actions, restarts, scaling, and public-access changes do not get an additional ZCP-specific confirmation gate. Most are reversible by another call. A few are operationally destructive even though they are not gated by ZCP (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. +| Operation | Gate | +| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Service deletion | Requires explicit approval in the same conversation, including the service name. Remote setup also blocks deleting the `zcp` service it is running in. | +| Wholesale service replacement after failed deploy history | The first request refuses, surfaces what would be replaced, and requires the agent to read failure evidence before asking you to confirm. | -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. +Deploys, env changes, lifecycle actions, restarts, scaling, and public-access changes do not get an additional ZCP-specific confirmation gate. They are normal project operations for a full-access token and should be reviewed through service events, logs, verification output, and team policy. -### Diagnose before destruct +Approval from a previous chat does not carry forward. A new conversation needs a new approval. -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. +## Diagnose before destruct -The rule prevents two failure modes: +When a service has recent failure history, ZCP enforces one recovery rule: read the platform evidence before destroying or replacing the service. -- **Delete-and-retry loops** — the agent decides the cleanest fix is to delete and re-import, masking the cause every time. -- **Replace-as-reset** — replacing a service from an import becomes a "make it like new" button that erases the failure context the next session needs. +The agent should inspect service events, build logs, runtime logs, and failure summaries, then either fix the cause or show you the evidence before asking for destructive confirmation. This prevents delete-and-retry loops that erase 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. +A service waiting for first code deploy is not the same thing as a failed service. The gate is about recorded failure history, not idle state. -Threat model and hard refusals: [Trust model](/zcp/security/trust-model). +Threat model and boundaries: [Trust model](/zcp/security/trust-model). ## Gotchas -- **Multi-project tokens are refused at startup, not at first deploy.** An account-wide 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 in remote setup; `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), and review service-scoped events, logs, deploy results, and git history before approving destructive recovery. +- **Account-wide full-access tokens are refused.** ZCP needs one project, not a broad account credential. +- **`zcp init` regenerates `.mcp.json`.** Re-add `ZCP_API_KEY` after rerunning it. +- **`GIT_TOKEN` is not `ZCP_API_KEY`.** One authorizes git provider access; the other authorizes Zerops project operations. +- **`ZEROPS_TOKEN` in GitHub Actions is not a GitHub PAT.** It is a Zerops API token for `zcli`. +- **A rotated token needs a restart.** The live ZCP process keeps the old environment value until it starts again. +- **A successful confirmation is still destructive.** Backups, git history, and service events are your recovery evidence; ZCP does not auto-rollback a confirmed deletion or replacement. ## 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. -- [Set up local ZCP](/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`. +- [Trust model](/zcp/security/trust-model) - the project boundary this page enforces. +- [Use remote ZCP workspace](/zcp/setup/hosted-workspace) - automatic token injection. +- [Set up local ZCP](/zcp/setup/local-agent-bridge) - local `.mcp.json` token setup. +- [Production boundary](/zcp/security/production-policy) - why production gets separate credentials. +- [GitHub integration](/references/github-integration) - CI secret usage with Zerops. diff --git a/apps/docs/content/zcp/security/trust-model.mdx b/apps/docs/content/zcp/security/trust-model.mdx index 645771f92..12998745c 100644 --- a/apps/docs/content/zcp/security/trust-model.mdx +++ b/apps/docs/content/zcp/security/trust-model.mdx @@ -1,96 +1,119 @@ --- -title: "Trust model" -description: "ZCP gives a coding agent operational access scoped to one Zerops project. Remote and local setup set different blast radii." +title: 'Trust model' +description: 'ZCP gives a coding agent operational access scoped to one Zerops project. Remote and local setup set different blast radii.' --- -The security model starts with one rule: a ZCP process operates one Zerops project. Remote and local setup share that project boundary, but they expose different surroundings to the agent. +The ZCP trust model starts with one rule: one ZCP process operates one Zerops project. The token decides the project boundary at startup, and ZCP refuses tokens that resolve to no project or multiple projects. -## Remote setup or local setup +That boundary is strong on the Zerops side. It does not make the agent harmless. A project-scoped token can still deploy, change env vars, restart services, read logs, scale services, and change public access inside that project when Zerops permissions allow it. Treat it like an operations credential for one project. -**Remote setup** is the project-contained path. +## Boundary summary -- ZCP runs in a `zcp@1` service inside the Zerops project. -- When **Include Coding Agent** is enabled, the bundled agent CLI runs there and is preconfigured to use ZCP. -- It uses a project-scoped token and reaches the project's private network. -- Runtime files are visible only when they are mounted into remote setup. -- Safety profile: **project-contained by design** - no direct access to your laptop, home directory, or other projects. +| Question | Answer | +| ------------------------------------ | ------------------------------------------------------------------------------------------- | +| What project can ZCP see? | Exactly one project resolved from `ZCP_API_KEY` at startup. | +| What can ZCP change? | Whatever Zerops RBAC grants that token inside the project. | +| What is outside reach? | Other projects, organization-wide settings, billing, and any operation Zerops RBAC rejects. | +| What network can remote setup reach? | The project's private network from inside the `zcp` service. | +| What network can local setup reach? | The Zerops API directly, plus project-private services only when your laptop VPN is up. | +| Who owns the agent login? | You. The agent subscription or model API key is separate from `ZCP_API_KEY`. | -**Local setup** is the supervise-the-client path. +Zerops [RBAC](/features/rbac) remains the authority. ZCP does not bypass platform permissions; it exposes project operations to the agent only through the token it was given. -- The agent runs on your laptop. -- It can touch the same project surface, **plus whatever the agent client can reach on your laptop**. -- It reaches project services over VPN, plus everything else your laptop can already reach. -- Safety profile: **supervise the agent client** - ZCP stays project-scoped, but the agent process inherits your user. +## Remote and local blast radius -Either path is the right choice in context. Local setup fits when you want to keep your editor, dotfiles, and toolchain — recognize the security model and set the agent client's permissions accordingly. +Remote setup and local setup share the same project boundary, but not the same surroundings. -## Project is the boundary +| Area | Remote setup | Local setup | +| -------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------- | +| Agent location | Inside the `zcp@1` service when **Include Coding Agent** is enabled | On your machine through your editor or CLI agent | +| Files visible to the agent | Workspace files and mounted runtime files | Local files and anything your client permits | +| Private service access | Project-private network without laptop VPN | `zcli vpn up ` from your machine | +| Local machine exposure | No direct access to your laptop | Agent client runs as your local user | +| Best safety posture | Keep ZCP in a development/staging project and avoid mounting unrelated files | Restrict client permissions, shell access, and filesystem scope | -One ZCP process works against one Zerops project. The boundary starts at authentication: at startup, ZCP 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 token form is scoped to exactly one project. +Remote setup is project-contained by design. Local setup is supervise-the-client by design. Either can be the right choice, but local setup requires more attention to your agent client's local permissions. -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. +## Credential ownership -| 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). | +There are three separate credential surfaces people often mix together: -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. +| Credential | Owner | Purpose | +| ----------------------------------- | ---------------------------- | ------------------------------------------------------ | +| `ZCP_API_KEY` | Zerops project token | Lets ZCP operate one Zerops project. | +| Agent subscription or model API key | You / the agent provider | Lets the coding agent run. Zerops does not provide it. | +| Git or CI credentials | You / your repository system | Lets finished work push to git or deploy through CI. | -## Remote setup specifics +Remote setup injects `ZCP_API_KEY` into the `zcp` service. Local setup reads it from the `.mcp.json` env block. In both paths, the agent account is still authenticated through the agent's own login flow. + +Details: [Tokens and credentials](/zcp/security/tokens-and-project-access). + +## What a project-scoped agent can do + +Inside the project, the agent can perform normal operational work when the token has permission: -Remote setup is the project-contained path. A few specifics: +- discover runtime and managed services, +- create or adjust services when the task requires it, +- read and write service env vars, +- deploy app code to runtime services, +- read build logs, runtime logs, and service events, +- restart, reload, stop, start, or scale services, +- enable or disable public access, +- prepare delivery paths such as git-push or CI handoff. + +This is why development and staging projects are the right place for ZCP. Production should be a separate project without a `zcp` service; see [Production boundary](/zcp/security/production-policy). + +## Remote setup specifics -- **`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. -- **Project access stays inside the project.** The `zcp@1` service can read project env and reach project services so the agent can build against real dependencies without you copying credentials around. -- **Filesystem reach is narrower than network reach.** The agent reaches project services over the private network, but only sees runtime files when they are mounted into remote setup. -- **A terminal in remote setup has the same project-scoped access as the agent.** Convenient, and the reason production stays in a separate project. +- **`zcp` service is not the app.** It is the workspace and control surface. Deploys target app runtimes, not the `zcp` service. +- **The service has project-level operational reach.** A terminal in the `zcp` service can use the same project-scoped access as the agent. Review the dashboard's additional changes before deploying the service; they are what give the workspace its project-scoped operating surface. +- **Network reach is broader than file reach.** The workspace can reach project-private services, but it only sees runtime files that are mounted into it. +- **Do not hand-edit `ZCP_API_KEY`.** Remote setup gets the value from Zerops. Manual replacement can break the intended one-project boundary. ## Local setup specifics -Local setup is the supervise-the-client path. A few specifics: +- **The agent inherits local reach.** ZCP is project-scoped, but the local agent client can read files, run commands, and use credentials allowed by your client settings. +- **Each local directory has its own config.** `.mcp.json` and `.zcp/state/` belong to one project directory. Launching from the wrong directory can connect the wrong project or no project. +- **VPN is outside ZCP authority.** Bringing up Zerops VPN needs your operating-system approval. ZCP cannot grant that for the agent. +- **`.env` files are snapshots.** They contain real project credentials and should stay out of git. -- **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 ZCP's authority.** Bringing up the Zerops VPN requires admin or root approval on your laptop. ZCP can tell 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. -- **Local setup 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. +## Human confirmation gates -Full setup: [Set up local ZCP](/zcp/setup/local-agent-bridge). +Most project operations do not get an extra ZCP-specific confirmation prompt. Deploys, env changes, restarts, scaling, and public-access changes are normal project operations and are audited through platform evidence. -## Audit surface +ZCP adds hard gates where the loss is not safely reversible from the conversation: -Zerops records platform-side evidence: service events, deploy/build results, runtime logs, lifecycle actions, scaling, and public-access changes. It does not record the agent's private reasoning, every shell edit, browser-helper actions, or prompt history outside your agent client. +| Operation | Gate | +| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| Service deletion | Requires explicit same-conversation approval that names the service. Remote setup also blocks deleting the `zcp` service it is running in. | +| Wholesale service replacement after failed deploy history | Requires the agent to read failure evidence first, summarize what would be replaced, and ask for explicit confirmation. | -When taking over from an agent, read evidence in this order: service-scoped events, runtime logs, deploy/verify result, then git history when delivery uses git-push. +Approval from an old chat does not carry forward. A new conversation needs a new approval. -| Surface | What it proves | What it does not prove | -|---|---|---| -| Service events | Deploy/build lifecycle, failures, restarts, scaling, public-access changes. | The exact source edit that caused them. | -| Build logs | Build commands, dependency install, compile/package failures. | Runtime request failures after deploy. | -| Runtime logs | Start crashes, port binding, request-time app errors. | Why the build failed. | -| Verify output | Whether platform reachability and requested behavior passed. | That unrelated app flows work. | -| Git history | Source changes pushed during git-push delivery. | Shell edits or uncommitted work that never reached git. | +## Audit evidence -Filter events and logs by service hostname when possible. Project-level timelines include unrelated services and older failures that can mislead an audit. +Zerops records platform-side evidence. It does not record the agent's private reasoning, every shell edit, browser-helper action, or prompt history outside your agent client. -## What ZCP refuses by design +When taking over from an agent, read evidence in this order: -ZCP refuses boundaries that should never be inferred. +1. service-scoped events, +2. build logs, +3. runtime logs, +4. deploy and verification output, +5. git history when delivery uses git-push. -| Refusal | Trigger | Recovery | -|---|---|---| -| Token resolves to multiple projects | Account-wide or multi-project token. | Use a token scoped to exactly one project. | -| Token resolves to no project | Token authenticates but has no project access. | Grant project access or generate a new project-scoped token. | -| Remote setup self-deletion | Agent tries to delete the `zcp` service it is running in. | Do it manually from outside that service if you really intend to remove ZCP. | -| Service deletion without named approval | Destructive delete requested without explicit same-conversation approval and service name. | Ask the human for explicit approval naming the service. | -| Destructive replacement before diagnosis | Import/override would replace a service with failed deploy history before logs/events were read. | Read the failure evidence first, then ask for confirmation with the evidence summary. | +| Surface | What it proves | What it does not prove | +| -------------- | ------------------------------------------------------------------------- | -------------------------------------------- | +| Service events | Deploy lifecycle, failures, restarts, scaling, and public-access changes. | The exact source edit that caused the event. | +| Build logs | Dependency install, build commands, compile/package failures. | Runtime request behavior after deploy. | +| Runtime logs | Start crashes, port binding, request-time app errors. | Why the build failed. | +| Verify output | Whether reachability and requested behavior passed. | That unrelated app flows work. | +| Git history | Source changes pushed during git-push delivery. | Uncommitted shell edits. | -Destructive confirmation details live in [Tokens and credentials](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions). +Filter by service hostname when possible. Project-level timelines can include unrelated services and older failures. ## Next steps -- [Tokens and credentials](/zcp/security/tokens-and-project-access) — token scope, storage, rotation, and confirmation gates. -- [Troubleshooting](/zcp/reference/troubleshooting) — recover when the agent or session state drifts. -- [Production boundary](/zcp/security/production-policy) — keep production outside the agent loop. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and confirmation gates. +- [Choose remote or local setup](/zcp/setup/choose-workspace) - compare blast radius before setup. +- [Production boundary](/zcp/security/production-policy) - keep production outside the agent loop. diff --git a/apps/docs/content/zcp/setup/choose-workspace.mdx b/apps/docs/content/zcp/setup/choose-workspace.mdx index 655affcfd..35ef1740c 100644 --- a/apps/docs/content/zcp/setup/choose-workspace.mdx +++ b/apps/docs/content/zcp/setup/choose-workspace.mdx @@ -1,77 +1,91 @@ --- -title: "Choose remote or local setup" -description: "Choose remote setup inside Zerops or local setup in your own editor/CLI." +title: 'Choose remote or local setup' +description: 'Choose remote setup inside Zerops or local setup in your own editor/CLI.' --- -Choose where the ZCP-backed agent runs. The same `zcp` binary gives the agent the same project-scoped Zerops operations in both paths, but the filesystem, network path, deploy source, and safety profile are different. +The same `zcp` binary runs in both setup paths. The ZCP model stays the same: one Zerops project, live project state, project-scoped operations, deploy and verify evidence, and proof or blocker at the end. + +The choice is where the agent and filesystem live. Most practical differences follow from that: remote work happens in a Zerops workspace that can reach or mount runtime services; local work happens from files and data on your machine, with a Zerops runtime linked when deploy and verify are needed. + +Remote setup is the default path for first use. The agent runs inside a `zcp@1` service in the Zerops project, not on your laptop. That isolation is the main safety advantage: the agent can be given broader command permissions inside a clean project workspace because the surrounding machine is a Zerops service, not your personal workstation. Remote setup also avoids local install, local MCP configuration, and laptop VPN for the first run. With **Include Coding Agent** and **Cloud IDE**, the project gets a preconfigured workspace with Claude Code and browser VS Code. You can also reach remote services from desktop editors that support Remote SSH or similar workflows; the browser IDE is the fastest default, not the only editor path. + +Local setup is for teams that deliberately want the agent running next to local files and local data. Use it when your normal editor, terminal, local tools, local git credentials, lower-latency feedback, or an agent client that remote setup does not cover should stay in charge. It is often a preference and ergonomics choice: some teams are simply faster in their established local environment. It can also reduce the need to keep a remote dev runtime running during inner-loop work, although runtime costs are usually small and a Zerops runtime is still needed when the agent should deploy and verify on Zerops. The tradeoff is safety: ZCP remains project-scoped, but the local agent client runs as your local user. :::info Public preview -Remote setup is the recommended first run. Local setup is real and supported, but the binary install, `.mcp.json`, and `zcp init` artifacts are still settling; expect setup details to move between releases. +Remote setup and local setup are both public preview. Local setup has more moving parts and is more likely to change: binary install path, `.mcp.json`, and `zcp init` artifacts may move between releases. ::: -Use remote setup for the first run and project-contained isolation. Use local setup when your checkout, editor, dev server, and git credentials should stay on your machine. Both paths let the agent discover services, wire the app, deploy, verify, and report proof from the same Zerops project. +## Remote vs local at a glance -## Short rule +| | Remote setup | Local setup | +| ------------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- | +| Where `zcp` runs | Inside the Zerops project in a `zcp@1` service | On your machine as the `zcp` binary | +| Where the agent runs | In the `zcp` service when **Include Coding Agent** is enabled | In your local editor or CLI agent | +| Default agent workspace | Browser VS Code plus Claude Code when enabled; desktop remote editors can connect over SSH | Your existing editor, terminal, and agent client | +| Filesystem the agent sees | Workspace files and mounted runtime files | Source files on your machine | +| Private service access | Project-private network, no laptop VPN for the workspace | `zcli vpn up ` from your machine | +| Deploy source | Remote workspace or mounted runtime source | Your local project directory | +| Git credentials | Credentials configured in the `zcp` service | Your local git credentials | +| ZCP token storage | Injected into the service by Zerops | `.mcp.json` env block in the project directory | +| Agent credentials | Entered into the bundled or installed agent in the service | Entered into your local agent client | +| Permission posture | Broader agent permissions stay contained in the project service | Agent permissions affect your local machine | +| Cost profile | Runs an extra project workspace service | No remote workspace service; deploy targets still cost while running | +| Safety profile | Project-contained by design | ZCP is project-scoped, but the agent client runs as your user | -**Remote setup** is the default first run: no local install, project-contained isolation, optional bundled agent, optional browser IDE. +## Starting points -**Local setup** fits when you want to keep your editor, shell, checkout, local dev server, and git credentials. +Remote setup starts from the Zerops dashboard: -## Remote vs local at a glance +- **Recipe with AI Agent environment.** Fastest way to try ZCP. The recipe creates app services plus the `zcp@1` workspace, Claude Code, and Cloud IDE together. +- **New project with ZCP enabled.** Start from a blank or custom project and toggle **Add Zerops Control Plane (ZCP) service** while creating it. +- **Existing project.** Add the `zcp` service next to services that already exist. -| | Remote setup | Local setup | -|---|---|---| -| Where `zcp` runs | Inside the Zerops project in a `zcp@1` service | On your machine as the `zcp` binary | -| Where the agent runs | In the `zcp` service when **Include Coding Agent** is enabled | In your local editor or CLI agent | -| Filesystem the agent sees | Runtime files when mounted into the remote setup | Your local working directory | -| Private service access | Project-private network, no laptop VPN | `zcli vpn up` from your machine | -| Deploy source | Project service / mounted runtime source | Your local checkout | -| Git credentials | Credentials configured in the `zcp` service | Your local git credentials | -| Local install | None for the first run | `zcp` binary, zCLI, project token, agent client config | -| Safety profile | Project-contained by design | ZCP is project-scoped, but the agent client runs as your user | -| Best first use | Yes | After you know you want local-agent control | -| Maturity | Public preview | Public preview, more WIP | +Local setup starts from a directory on your machine. That can be: -## What changes in practice +- **Empty local directory.** Start with no app code yet; the agent can create the app shape and ask ZCP for the needed Zerops services. +- **Existing project directory.** Run `zcp init` beside code, tools, data, and git credentials you already use locally. +- **Recipe prepared for local setup.** When a recipe offers a local-oriented environment, use it to create the Zerops services and wiring, then run the agent locally from the directory that should own app work. -In remote setup, Zerops packages the `zcp` binary inside a `zcp@1` service in the project. Enable **Include Coding Agent** when you want the service to add the bundled agent CLI and preconfigure it to use ZCP. Enable **Cloud IDE** when you also want browser-based VS Code. Runtime files can be mounted into the service, and managed services are reachable over the project-private network. +All local paths converge on the same setup: install `zcp`, run `zcp init`, add a project-scoped token, bring up VPN when private services are needed, and launch your local agent from that directory. -In local setup, you install the same `zcp` binary on your machine and initialize it in a project folder. Your local agent client talks to ZCP from that folder. ZCP talks to the Zerops API directly, while your local app and shell need `zcli vpn up` to reach managed services by hostname. Full install flow lives in [Set up local ZCP](/zcp/setup/local-agent-bridge). +The details live in the setup pages: -Remote setup costs one small `zcp` service in the project, billed like any other Zerops service. Local setup costs local installation and a VPN path, plus a local `.env` snapshot with real project credentials that must stay out of git. +- [Use remote ZCP workspace](/zcp/setup/hosted-workspace) +- [Set up local ZCP](/zcp/setup/local-agent-bridge) -## Starting points +## Runtime layout still matters -Remote setup has two common entry points: +Setup choice is not the same thing as app runtime layout. -| Starting point | Use when | What happens | -|---|---|---| -| Recipe with **AI Agent** environment | You want the shortest first run or a known stack baseline. | Zerops deploys app services plus `zcp@1`, Coding Agent, and Cloud IDE together. | -| New or existing project with **Add Zerops Control Plane (ZCP) service** | You want a custom project, or a project already has services. | Zerops adds the `zcp` workspace service; the agent then uses existing services or creates missing ones from the app request. | +Remote or local only answers where the agent and `zcp` process run. The project can still have one app runtime, a dev+stage pair, a single stage target for local files, or no linked runtime yet. -Local setup starts from a local checkout. Run `zcp init` in that folder, add a project-scoped token, and connect your local agent client. The project may already have Zerops runtimes, or the agent may need to create or link a runtime before deploys can happen. +Common shapes: -## App runtime layout +| Shape | What it means | Typical names | +| -------------------------- | ------------------------------------------------------------------------------------ | ---------------------------- | +| Dev + stage | The agent iterates on dev, then proves the result on a separate stage runtime. | `appdev` + `appstage` | +| One mutable dev runtime | Fastest shape for experiments and early builds. | `appdev` | +| One app runtime | A small project with no dev/stage split. | `app` | +| Local files + stage target | Files on your machine are the source; a named Zerops runtime receives deploys. | local directory + `appstage` | +| Local files only | ZCP can inspect and generate env snapshots, but deploy needs a linked runtime later. | local directory | -After setup, ZCP reads how app runtimes are arranged. You usually state the outcome, not an internal label: use the dev+stage pair, deploy to a named stage runtime, use the single app runtime, or link this local checkout to a stage target. +Stage is a review or promotion target, not production. Production should live in a separate Zerops project; see [Production boundary](/zcp/security/production-policy). -| Shape | What it means | Typical names | -|---|---|---| -| Dev + stage | The agent can iterate on dev, then prove the result on a separate stage runtime. | `appdev` + `appstage` | -| One mutable dev runtime | Fastest project shape for experiments and early builds. | `appdev` | -| One app runtime | A small project with no dev/stage split. | `app` | -| Local checkout + Zerops target | Your local files are the source; a named Zerops runtime receives deploys. | local checkout + `appstage` | -| Local checkout only | ZCP can inspect and generate env snapshots, but deploy needs a linked runtime later. | local checkout | +The stage hostname is explicit. The agent should read project state and use confirmed hostnames, not invent `appstage` from `appdev`. -Stage is a review or promotion target, not production. Production should live in a separate Zerops project; see [Production boundary](/zcp/security/production-policy). +## Switching later + +You can change setup path later without changing the project boundary. -The stage hostname is explicit. The agent should use confirmed project state, not invent `appstage` from `appdev`. +- Start remote for evaluation, then move to local when you want local files, local tools, or lower-latency editing. +- Start local for a repo-first project, then add remote setup for a project-contained browser workspace. +- Keep both paths pointed at the same development project only when your team understands which source tree is authoritative for deploys. -Exact runtime-layout labels are listed in the [Glossary](/zcp/glossary#project-and-runtime-terms). Zerops service scaling mode, such as `HA` or `NON_HA`, is an individual service setting and is unrelated to this table. +Avoid using either path against production. Promotion should be a separate CI or release step into a separate production project. ## Next steps -- [Set up remote ZCP](/zcp/setup/hosted-workspace) +- [Use remote ZCP workspace](/zcp/setup/hosted-workspace) - [Set up local ZCP](/zcp/setup/local-agent-bridge) -- [Build with ZCP](/zcp/workflows/build-with-zcp) - [Trust model](/zcp/security/trust-model) +- [Production boundary](/zcp/security/production-policy) diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index 9edea213d..2d001ccbf 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -1,125 +1,134 @@ --- -title: "Set up remote ZCP" -description: "Add a zcp@1 service to a Zerops project and choose whether it includes a coding agent, Cloud IDE, or both." +title: 'Use remote ZCP workspace' +description: 'Open or add the project-contained zcp@1 workspace with Claude Code, browser VS Code, and project-scoped ZCP access.' --- -Remote setup is a `zcp@1` service inside your Zerops project. It keeps ZCP, project-scoped credentials, private-network access, and optional agent tooling inside the project boundary. +Remote setup creates an isolated agent workspace inside the Zerops project. The `zcp@1` service runs the same `zcp` binary used by local setup, but the agent, terminal, and optional browser IDE operate from a clean project service instead of your laptop. -Enable **Include Coding Agent** when you want Zerops to add the bundled agent CLI and preconfigure it to use ZCP in that service. The agent authentication is still yours: sign in with the agent's subscription login or provide its API credentials. Enable **Cloud IDE** when you also want browser-based VS Code for watching or intervening. +That isolation is the reason remote setup is the recommended first path. Zerops can preconfigure a more permissive agent workspace, project-private networking, and project-scoped credentials without exposing your local machine. -After provisioning, the service is ready for your first app instruction. If **Cloud IDE** is enabled, you can open the browser workspace from the dashboard; otherwise use the access method configured for the service. +In most dashboard paths, there is little local setup to do. A recipe or project toggle creates the workspace service, injects the ZCP token, and can include Claude Code plus browser VS Code. This page is about choosing that workspace path, opening it, and understanding what belongs in the workspace versus what belongs in app runtime services. -Remote and local setup have the same ZCP project boundary but different work surfaces. The comparison lives in [Choose remote or local setup](/zcp/setup/choose-workspace); the broader human development modes live in [Local & Remote Development](/features/local-remote-development). +App code still deploys to runtime services such as `appdev`, `appstage`, or `app`; the `zcp` service is the workspace and control surface, not the application runtime. -## Two paths to remote setup +For the local alternative, see [Set up local ZCP](/zcp/setup/local-agent-bridge). For the tradeoffs, see [Choose remote or local setup](/zcp/setup/choose-workspace). -Zerops provisions the service for you - no local install, no MCP config to write by hand, no YAML to paste. Pick the path that matches what you're starting from: +## What the remote workspace gives you -- **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 a `zcp@1` service with **Include Coding Agent** enabled. -- **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. +| Part | What it does | +| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `zcp@1` service | Runs the `zcp` binary inside the project and exposes project-scoped ZCP operations to the agent. | +| `ZCP_API_KEY` | Injected automatically by Zerops, scoped to this one project. Do not set it by hand in remote setup. | +| **Include Coding Agent** | Installs and preconfigures the bundled agent CLI. The current dashboard flow bundles Claude Code; support for more agents is planned. | +| Claude Code auth | Uses your Claude subscription login or API token. Zerops wires Claude Code to ZCP, but the agent account stays yours. | +| **Cloud IDE** | Adds browser-based VS Code for editing, terminals, and supervising the agent session. | +| Workspace toolchain | Provides a shell, `zcli`, common CLI tools, database clients, browser helpers, and the project-local ZCP connection as a starting workspace. | +| Project-private network | Lets the workspace reach project services such as databases and caches by hostname. Your laptop does not need VPN for the remote workspace. | +| Permission boundary | Keeps broad agent command permissions inside the Zerops project service instead of on your laptop. The bundled remote profile is intentionally low-friction; do not copy that posture to local setup. | -Both paths produce the same base result: a `zcp@1` service inside the project with `ZCP_API_KEY` injected automatically and scoped to this project. **Include Coding Agent** adds the preconfigured agent CLI. **Cloud IDE** adds the browser editor. Your model subscription, API key, or agent login is not a Zerops credential; it belongs to the agent CLI you choose to run. +The model account, GitHub account, package registry login, and other external credentials are still yours to provide to the tools that need them. -## Path A — recipe + AI Agent environment +## Get a remote workspace -Recipes are the quickest path when their stack matches the runtime layout you need. +There are three common dashboard paths. They all produce the same kind of `zcp@1` workspace service. -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. +### Recipe with AI Agent environment -The recipe ships its full service set; the **AI Agent** environment is what adds the `zcp@1` service with **Include Coding Agent** enabled alongside. +Use this path when you want the shortest first run or a known stack baseline. -If you pick a non-AI environment (Remote CDE, Local, Stage, Small Production, Highly-available Production) the recipe still deploys, but no `zcp` service with a bundled agent is added. Add ZCP afterward via Path B. +1. Open the [Zerops recipes catalog](https://app.zerops.io/recipes). +2. Open a recipe that offers an **AI Agent** environment, for example [Laravel showcase](https://app.zerops.io/recipes/laravel-showcase?environment=ai-agent). +3. Select **AI Agent**. +4. Keep **Coding Agent** and **Cloud IDE** enabled for the first run. +5. Deploy the recipe. -## Path B — toggle the ZCP service +The recipe creates app services and managed services, then adds the `zcp` workspace. After the workspace is ready, normal app work continues through [Build with ZCP](/zcp/workflows/build-with-zcp). -Use this for a custom project with no recipe, or when you already have a Zerops project running. +### New project with ZCP enabled -For a **new project**: +Use this path when you want a blank or custom 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. Keep **Include Coding Agent** enabled to add and preconfigure the bundled agent inside the service. Use **Configure** when you need to choose authentication or adjust Cloud IDE access; otherwise leave the defaults and submit. -5. Submit. +2. Enter the project name, region, and tags. +3. Toggle **Add Zerops Control Plane (ZCP) service**. +4. In **ZCP CONFIGURATION**, keep **Include Coding Agent** enabled if you want bundled Claude Code. +5. Keep **Cloud IDE** enabled if you want browser VS Code. +6. Submit the project. -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. +The result is a project with a `zcp@1` workspace. The app runtime and managed services may still need to be created later as part of normal ZCP app work. -## Open the service +### Existing project -Open the `zcp` service from the Zerops dashboard after it reaches running state. +Use this path when the project already has runtime or managed services. -1. Open the project in the [Zerops dashboard](https://app.zerops.io/). -2. Open the `zcp` service. -3. If **Cloud IDE** is enabled, open the service workspace URL. Code-server launches in a new tab. If the service is exposed on a `.zerops.app` subdomain, see [Public access](/features/access) for the routing model. - -If **Cloud IDE** is disabled, remote setup can still exist as a project-contained service, but there is no browser editor to open. Use the access method configured for the service, such as SSH, or enable Cloud IDE when you want a browser workspace. +Add a `zcp` service from the project dashboard the same way you add any other Zerops service. The workspace appears next to the existing services and receives project-scoped ZCP access from the platform. -## Run the agent +Do not treat the existing services as automatically correct for the next app task. The agent should read project state and use or change services only as the requested work requires; that workflow belongs in [Build with ZCP](/zcp/workflows/build-with-zcp). -When **Include Coding Agent** is enabled, the bundled agent CLI is installed and already wired to this project through ZCP. In the Cloud IDE path, the terminal has the bundled agent available with `ZCP_API_KEY` in its environment. You still authenticate the agent itself with that agent's own login flow, subscription, or API key. +## Configure the ZCP service -For a connection sanity check, ask the bundled agent: +The dashboard configuration controls what the remote workspace includes. -```text -List the services in this project and tell me which are runtimes versus managed dependencies. -``` +| Option | What to decide | +| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Coding Agent | Enable it when you want Claude Code installed and preconfigured for this project. Authenticate with your Claude subscription login or API token. | +| Cloud IDE | Enable it when you want browser VS Code. Choose public access when you need browser access without VPN; choose VPN-only access when the workspace should stay private. | +| Additional changes | Review the platform changes before deploy. Zerops creates and injects the project-scoped token ZCP needs, and enables the workspace access required for project operations. | +| Workspace access | Keep the default remote permission posture when you want low-friction agent work inside the isolated project workspace. Do not mirror that posture onto local setup. | +| Team-specific tooling | Add package installs, dotfiles, private MCP servers, helper processes, or derived images when your team needs them persistently. | -A working setup answers with the runtime services (for example `appdev`, `appstage`) and managed services (for example `db`), and says whether the project has one runtime or a dev+stage pair. +These choices affect the workspace, not the app runtime. Runtime services own app builds and deploys. -For real work, ask for the app behavior, not the Zerops procedure: +## Open the workspace -```text -Build a task board where tasks stay saved after refresh. -``` +After the `zcp` service reaches running state: -Discovery, service setup, deploy, verification, and final reporting happen behind that app request. +1. Open the project in the [Zerops dashboard](https://app.zerops.io/). +2. Open the `zcp` service. +3. Use **Browser VS Code** when **Cloud IDE** is enabled. +4. Complete the Claude Code login or API-token flow if prompted. -If **Include Coding Agent** is disabled, the `zcp@1` service exists but no preconfigured agent is waiting inside it. You can enable the option, install your own agent CLI in the service, or use [local ZCP](/zcp/setup/local-agent-bridge) from your own agent client. +After authentication, Claude Code is installed, connected to this project's ZCP service, and ready to work from the browser VS Code terminal. The first app prompt belongs in [Build with ZCP](/zcp/workflows/build-with-zcp), not in setup. -## Bring your own agent or tools +First load can take a minute while the image, Cloud IDE, and bundled agent finish starting. If the page opens before the service is ready, wait until the service is running and reload. -Remote setup is open by design. Zerops provides the project-scoped ZCP connection; the agent runtime around it is yours to shape. +If the service was exposed on a `.zerops.app` subdomain, public routing follows the normal Zerops [public access](/features/access) model. The workspace still uses the project-scoped token inside the service. -You can: +## Use a desktop remote editor -- authenticate the bundled agent with your own subscription login or API credentials, -- install another agent CLI in the `zcp` service and configure it to use the same ZCP project connection, -- add other MCP servers or helper processes for private APIs, internal tools, design systems, or repositories, -- edit agent instructions such as `CLAUDE.md` for project-specific policy and working conventions, -- keep extra dotfiles, shell config, package managers, linters, or framework tools in the service. +Browser VS Code is the quickest way into the remote workspace, but it is not the only editing path. Editors that support remote development can connect over SSH to the `zcp` service or directly to runtime services, depending on how your team wants to work. -ZCP is the Zerops project API available to the agent. It does not own the model account, replace the agent client, or prevent you from adding your own tooling. +Common options include VS Code Remote SSH, JetBrains Gateway, Cursor, Zed, and plain SSH. This keeps the agent and project-private network inside Zerops while letting you use a local desktop editor UI. -After the first setup pass, runtime services can be SSHFS-mounted into the service filesystem with one folder per runtime. 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 remote setup. +For the broader development model, see [Local & Remote Development](/features/local-remote-development#native-ide-over-ssh). -If you added ZCP to an existing project, the agent should inspect existing services and use them instead of creating duplicates. That is handled as part of the first product prompt; the details are explained in [Service setup behind the prompt](/zcp/workflows/create-or-adopt-services). +## Runtime file access -When Cloud IDE is enabled, the browser workspace is persistent. Close the browser tab and reopen the workspace link later; editor state, open files, and the agent session survive across reconnects as long as the `zcp` service is running. +Remote setup can mount runtime service filesystems into the `zcp` workspace after the first setup pass. Each mounted runtime appears as its own folder. Editing a mounted file changes the file inside that runtime service, with no upload step from your laptop. -## If connection fails +Filesystem reach is intentionally narrower than network reach. The workspace can reach project services over the private network, but it only sees runtime files that are mounted into it. -If the agent can't reach ZCP, the service may still be starting. Wait for it to reach running state in the dashboard and reload the workspace. If **Include Coding Agent** was not enabled, add it or use local ZCP from your own agent client. +## Make customization persistent -At this point, the agent can receive the first app instruction. The [Quickstart](/zcp/quickstart) shows the shortest end-to-end run. +The `zcp@1` service is a normal Zerops service. One-off shell installs disappear when the service is rebuilt unless you make them part of the service setup. -## Customize remote setup +Use these patterns: -Remote setup is a normal Zerops service running the `zcp@1` base image. The configuration generated by **Include Coding Agent** and **Cloud IDE** is a starting point, not a sealed product. +- **Small additions:** put package installs, dotfiles, or bootstrap scripts into service init commands. +- **Team-standard workspace:** build a derived image based on `zcp@1` with required tools already present. +- **Helper processes:** run project-local helpers next to the agent and editor workspace when your team needs private integrations. -Common patterns: +Avoid putting app runtime build steps into the `zcp` service. Runtime services own app builds and deploys. -- **Add team tools.** Install missing packages in the `zcp` service. To make them persistent, move the install commands into the service `run.initCommands`. -- **Preload developer config.** Put dotfiles, shell config, framework bootstrap, or onboarding scripts into init commands so every workspace starts the same way. -- **Run helper processes.** Add side processes next to the agent and Cloud IDE: cache warmers, inspectors, internal CLIs, or your own local helpers. -- **Fork the base image.** When init commands become too large, build a hardened image based on `zcp@1` with team-standard tools baked in. -- **Add another MCP/helper server.** The service can run project-local helpers alongside ZCP when your team needs a private integration. +## Boundaries to keep -Keep this separate from app runtime work. Deploy app code to runtime services, not to `zcp`. +- **The `zcp` service is not the app.** Deploy app code to runtime services, not to the workspace service. +- **Do not set `ZCP_API_KEY` manually in remote setup.** Zerops injects it. A manual override can break the project boundary. +- **The agent account is separate from Zerops.** If Claude Code asks you to sign in, use your Claude subscription or model API credential. +- **Remote setup is not production policy.** Use it in development or staging projects. Production should live in a separate project without a `zcp` service; see [Production boundary](/zcp/security/production-policy). -## Gotchas +## Next steps -- **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. If the agent targets `zcp` as the app runtime, stop and correct the target. -- **Don't set `ZCP_API_KEY` by hand in remote setup.** It's platform-injected. Manual override risks breaking the agent's access. (Local setup 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 the bundled agent. Until the service reaches running state, the workspace URL may return an empty page or error. +- [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. +- [Trust model](/zcp/security/trust-model) - what the project boundary covers. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - how `ZCP_API_KEY`, git credentials, and CI secrets differ. diff --git a/apps/docs/content/zcp/setup/local-agent-bridge.mdx b/apps/docs/content/zcp/setup/local-agent-bridge.mdx index d44913b18..4ff204835 100644 --- a/apps/docs/content/zcp/setup/local-agent-bridge.mdx +++ b/apps/docs/content/zcp/setup/local-agent-bridge.mdx @@ -1,50 +1,83 @@ --- -title: "Set up local ZCP" -description: "Install the zcp binary, run zcp init in your project, and let a local coding agent operate one Zerops project." +title: 'Set up local ZCP' +description: 'Install the zcp binary, run zcp init in your project, and let a local coding agent operate one Zerops project.' --- -Local setup runs the `zcp` binary on your laptop, initialized in one project directory. The agent edits your checkout, your tools run the dev server, and ZCP deploys from your working directory to the linked Zerops runtime. +Local setup runs the `zcp` binary on your machine and connects it to one project directory. Your local agent works next to files and data on your machine, your editor and terminal stay local, your git credentials stay local, and ZCP gives the agent project-scoped Zerops operations. -`zcp init` writes the local MCP configuration for that folder, so a compatible local agent client can talk to ZCP as the `zerops` MCP server. With `zcli vpn up `, your laptop also joins the project's private network - the same network your services use. +Use local setup when your normal local environment should stay in charge: a desktop editor, local files, local data, familiar terminal tooling, local git credentials, lower-latency feedback, or an agent client that is not covered by remote setup. It can also reduce the need to keep a remote dev runtime running during inner-loop development, although a Zerops runtime is still needed when the agent should deploy and verify on Zerops. + +The tradeoff is local machine exposure. ZCP is still scoped to one Zerops project, but the agent client runs as your local user and can reach whatever your client permissions allow. If you are trying ZCP for the first time, [remote setup](/zcp/setup/hosted-workspace) is usually safer and faster. :::warning Public preview Local setup is the part most likely to change. The binary install path, `.mcp.json` shape, and files written by `zcp init` are still settling between releases. ::: -Use this path when you already have a comfortable local setup - your editor, your shell, your dev server - and want a coding agent that can drive Zerops from the same machine. If you're evaluating ZCP for the first time, [remote setup](/zcp/setup/hosted-workspace) is a faster start. +## What local setup changes -You don't need ZCP at all to develop locally against a Zerops project: `zcli vpn up` plus your editor is enough. ZCP is what you add when you also want a coding agent operating the project from the same machine: discovering services, generating local env snapshots, deploying, reading logs, verifying. +| Area | Local setup behavior | +| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ZCP process | Runs on your machine as `zcp serve`, launched by your agent client from the project folder. | +| Agent process | Runs as your local user through your editor or CLI agent. ZCP stays project-scoped, but the agent client can reach whatever your local permissions allow. | +| Source files | The agent edits files on your machine. Deploys use your working directory as the source. | +| Local tooling | Your normal terminal, framework CLI, editor runner, and local feedback loop stay in charge. | +| Private network | Your local app and shell reach project services through `zcli vpn up `. | +| Project token | You add a project-scoped `ZCP_API_KEY` to the local `.mcp.json` env block. | +| Agent credentials | Stay with your local agent client. ZCP does not see the agent subscription or model login. | +| Git credentials | Your local git CLI, SSH agent, or credential helper handles pushes. | +| Cost profile | No remote `zcp` workspace service. Deploy targets and managed services still cost when they run. | -Security note: ZCP itself stays project-scoped, but the agent client runs as your local user. Set your client permissions accordingly. +Security note: local ZCP cannot protect your laptop from the agent client. Set your client permissions, filesystem allowlists, and shell approval rules as you would for any local coding agent. ## 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 compatible local agent client installed and logged in. ZCP doesn't see the agent client's subscription or login credentials. +- [zCLI](/references/cli) installed and authenticated on your machine. +- A compatible local agent client installed and logged in. +- A **project-scoped Zerops token** for exactly one project. Multi-project tokens are refused at startup. +- A local directory where the agent should run. + +You do not need ZCP just to develop locally against Zerops services. `zcli vpn up` plus your editor is enough for human local development. Add ZCP when a local coding agent should also discover services, generate env snapshots, deploy, read logs, and verify the project. + +## Choose a local starting point + +Local setup is not limited to an existing app directory. Pick the source that matches the work: + +- **Empty local directory.** Use this when you want the agent to create the app shape from a product request, then use ZCP to provision or select Zerops services. +- **Existing project directory.** Use this when the app code, local data, editor setup, git credentials, and team tooling already live on your machine. +- **Recipe prepared for local setup.** Some recipes provide local-oriented environments in addition to remote AI Agent environments. Use that path when you want a known Zerops service baseline from a recipe, but want the agent, editor, files, and local tooling to stay on your machine. -## Get a project-scoped Zerops token +After that choice, the local mechanics are the same: initialize ZCP in the chosen directory, add the project-scoped token, use VPN when private service access is needed, and link a runtime when the agent should deploy. -ZCP needs a Zerops API token scoped to **one project**. Multi-project tokens, including account-wide full-access tokens, are refused at startup. Generate one in the Zerops dashboard, then keep the value handy for the local MCP configuration step. Full walkthrough, rejection rules, and rotation: [Tokens and credentials](/zcp/security/tokens-and-project-access). +## 1. Get a project-scoped Zerops token -## Install `zcp` +ZCP needs a Zerops API token scoped to exactly one project. For normal agent work, use a single-project token with full access to that project. Read-only tokens can authenticate but fail on deploys, env changes, lifecycle actions, and other mutations. + +Token generation, rejected shapes, and rotation are covered in [Tokens and credentials](/zcp/security/tokens-and-project-access). Keep the token value available for the `.mcp.json` step below. + +## 2. Install `zcp` ```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. +The installer downloads the latest release for your platform into `~/.local/bin`, or `/usr/local/bin` when run as root. Verify the install: -## Run `zcp init` in your project folder +```bash +zcp version +``` -In a terminal in your project's working directory: +If your shell cannot find `zcp`, add the install directory to `PATH` and reload the shell. + +## 3. Run `zcp init` in the project folder + +From the local directory the agent should operate: ```bash zcp init ``` -`zcp init` writes the local MCP configuration and agent instructions for this project. +`zcp init` writes the local MCP configuration and agent instructions for this project directory. Current generated artifacts: @@ -52,71 +85,99 @@ Current generated artifacts: - `CLAUDE.md` - agent instructions for operating ZCP in this project. - `.claude/settings.local.json` - Claude Code per-project settings when that client is used. - `~/.config/zerops/aliases` plus a shell-rc sourcing line - helper aliases for launching the agent from this project. -- `.zcp/state/` - appears later, on the first workflow/adoption/session state write. +- `.zcp/state/` - created later when ZCP first writes local project state. + +Re-running `zcp init` may refresh generated config. `CLAUDE.md` preserves edits outside managed markers, but `.mcp.json` is regenerated from the token-less template. If you rerun it, re-check the `ZCP_API_KEY` env block before launching the agent again. + +## 4. Add `ZCP_API_KEY` to `.mcp.json` + +`zcp init` writes a token-less `.mcp.json`. Add the project token under the `env` block: + +```json +{ + "mcpServers": { + "zerops": { + "command": "zcp", + "args": ["serve"], + "env": { + "ZCP_API_KEY": "" + } + } + } +} +``` -Re-running `zcp init` may refresh generated config. `CLAUDE.md` preserves edits outside managed markers, but `.mcp.json` is rewritten from the token-less template. After rerunning it, confirm `.mcp.json` still contains `ZCP_API_KEY` before launching the agent. +Add `.mcp.json` to `.gitignore`. It contains a live project credential and should not leave the machine. Each project directory should have its own `.mcp.json` and its own project token. -## Configure `zcp` as the local MCP server +The server name `zerops` is intentional. Do not rename that key unless your agent client explicitly requires a different name and you understand the resulting prompt/instruction changes. -`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 your agent client from the 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--remote-vs-local). +## 5. Launch the agent from the project root -The agent client starts and lists the MCP servers it found. The `zerops` server should be in the list; that is the local ZCP connection. +Start your local agent client from the same directory that contains `.mcp.json`. The client should list `zerops` as an available MCP server. -### Sanity check +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 the agent client from the wrong directory. +A working connection answers with the project's runtime and managed services. If the agent cannot reach ZCP, check the launch directory, token scope, and whether the client actually loaded `.mcp.json`. -## Connect private services with `zcli vpn up` +## 6. Bring up the Zerops VPN -Your laptop reaches managed services (databases, caches, storage) through the Zerops project [VPN](/references/networking/vpn). ZCP 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: +ZCP can talk to the Zerops API without VPN. Your local app, shell, tests, database clients, and framework commands need VPN to reach project-private service hostnames such as `db` or `cache`. ```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. +VPN setup needs admin or root approval on macOS and Linux. ZCP can tell the agent which command is needed, but it cannot approve or start the VPN for you. -After the VPN is up, project services resolve by hostname (`db`, `cache`, etc.) from your laptop. +After the tunnel is up, project service hostnames resolve from your machine. Laptop sleep, network changes, and idle time can drop the tunnel; run the command again when local service connections start failing. -## Local env bridge +## 7. Generate a local env snapshot when needed -When a local app needs project credentials, ask for the product change and let ZCP generate the local env bridge as part of that work. For a standalone sanity check, you can ask: +When the local app needs project credentials, ask the agent for the app work and let ZCP generate the env bridge as part of that work. For a standalone check: ```text Use ZCP to generate a .env file for my local app. ``` -ZCP resolves the project env references your app would see when deployed and writes a `.env` snapshot in your working directory. Your local app uses the same hostnames and credentials, reached over VPN. Regenerate after changing project env variables. +ZCP resolves the env references your app would see when deployed and writes a `.env` snapshot in the working directory. The file is a snapshot, not a live sync. Regenerate after changing project env variables. + +Keep `.env` out of git. It contains real project connection values. -## Linked deploy target +## 8. Link a deploy target -Local setup needs one Zerops runtime linked as the deploy target, often a stage runtime, 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 setup needs a Zerops runtime when the agent should deploy from your working directory. A stage runtime is the usual target because it lets local work prove itself before any production promotion path. + +If the project has exactly one runtime, ZCP can use it automatically. If multiple runtimes exist, the agent should ask which one to link. Answer by hostname, for example: + +```text +Link this local directory to appstage for deploys. +``` -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. Ask the agent to link a runtime by hostname when ready. +Without a linked runtime, ZCP can still inspect services and generate env snapshots, but deploys from your local directory need a target first. -With a linked runtime, ZCP can deploy from your working directory. Without one, it can still inspect the project and generate env snapshots, but deploys need a target runtime first. +## What stays outside ZCP -## What stays your tool's job +ZCP does not replace your local feedback loop. Vite, Valet, Docker Compose, your IDE runner, framework CLIs, local fixtures, and test data stay under your normal local tooling. -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 does not mount Zerops runtime filesystems on your laptop. Remote setup can mount runtime files into the workspace; local setup works from files on your machine. If you need to inspect runtime files from your machine, use [SSH](/references/networking/ssh) directly. -ZCP doesn't mount Zerops runtime filesystems on your laptop. Remote ZCP can give the agent SSHFS access; local ZCP doesn't. If you need to read runtime files from your machine, use [SSH](/references/networking/ssh) directly. +ZCP does not own your git credentials. In local setup, your local git CLI, SSH agent, or credential helper handles pushes. ## 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; the agent client 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 the agent client both expect it. -- **The `.env` is a snapshot.** Regenerate after changing project env variables, not before. +- **Launch directory matters.** Start the agent from the folder that contains `.mcp.json`; otherwise it may connect to no ZCP server or the wrong project. +- **Multi-project tokens are rejected.** Use a token scoped to exactly one project. +- **`zcp init` can remove the token from `.mcp.json`.** Re-add `ZCP_API_KEY` after rerunning init. +- **VPN is separate from ZCP auth.** The agent may reach ZCP while your app still cannot reach `db` because VPN is down. +- **`.env` is a credential snapshot.** Regenerate after project env changes and keep it out of git. - **Production stays out of this loop.** Local ZCP is for development and staging projects. ## Next steps -- [Deploy, verify, and fix](/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 remote or local setup](/zcp/setup/choose-workspace) - remote vs local and runtime layouts. +- [Choose remote or local setup](/zcp/setup/choose-workspace) - compare setup paths. +- [Build with ZCP](/zcp/workflows/build-with-zcp) - normal app work after setup. +- [Tokens and credentials](/zcp/security/tokens-and-project-access) - token scope, storage, rotation, and destructive confirmations. 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 3d7cdb6b5..15f0217e3 100644 --- a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx +++ b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx @@ -1,17 +1,17 @@ --- -title: "Deploy, verify, and fix" -description: "How the agent uses ZCP to edit code, deploy through Zerops, read evidence, fix failures, and prove behavior." +title: 'Deploy, verify, and fix' +description: 'How the agent changes app code, deploys through Zerops, reads evidence, fixes failures, and proves behavior.' --- After the target runtime and dependencies are known, the agent can change code and `zerops.yaml`, deploy the runtime, fix failures from evidence, and verify the result. -A ZCP task is done only when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the working URL or a concrete blocker. +A ZCP app task is done only when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the working URL or a concrete blocker. Localhost is not delivery. A local dev server can speed up implementation, but the completion proof comes from the Zerops runtime or the linked deploy target. -## What the agent does +## The expected loop -ZCP treats deploy as diagnostics and verification as the completion gate. The user does not have to script this sequence; it is the expected path behind an app prompt. +ZCP treats deploy as diagnostics and verification as the completion gate. You do not have to script this sequence; it is the expected path behind an app prompt. - **Runtime scope.** The agent identifies which runtime changed. - **Code and `zerops.yaml`.** The agent changes app files and runtime config where needed. @@ -24,15 +24,15 @@ A green build or running runtime is not done until the requested endpoint, UI, o ## Runtime scope is a precondition -Before editing, the agent should make the runtime service visible: `appdev`, `appstage`, `app`, or the linked runtime in a local setup. Managed services such as databases and caches are dependencies, not deploy targets. +Before editing, the agent should name the runtime service that will run app code: for example `appdev`, `appstage`, `app`, or the linked runtime in a local setup. Managed services such as databases and caches are dependencies, not deploy targets. If the agent starts writing files before it knows the runtime scope, treat that as a correction signal: it should read ZCP status and identify the target before continuing. ## Deploy -The first deploy uses the direct ZCP deploy path. Delivery choices such as git-push or external handoff apply after a verified deploy exists. +The first deploy uses ZCP's direct deploy path so the agent can produce and verify a real running result. Delivery choices such as git or external handoff apply after that verified result exists. -A direct deploy waits for the Zerops build/deploy process to finish and for the runtime to be ready enough to verify. Git-push delivery is different: the push returns first, then the agent observes the build integration or events that follow and verifies afterward. See [Choose how finished work ships](/zcp/workflows/delivery-handoff). +A direct deploy waits for the Zerops build/deploy process to finish and for the runtime to be ready enough to verify. If finished work later ships through git, the push and the follow-up build are separate from this first proof step. See [Choose how finished work ships](/zcp/workflows/delivery-handoff). Build and runtime evidence are separate: @@ -44,41 +44,53 @@ Remote setup can deploy multiple hosted runtime services in parallel when the ta Deploy failures point to different evidence: -| Failure point | Read first | -|---|---| -| Build failed | Build logs and `zerops.yaml` build steps | -| Runtime started then crashed | Runtime logs and start command | -| App is up but route fails | Runtime logs at request time and behavior check output | -| Local app cannot reach managed services | VPN state and generated `.env` values | +| Failure point | Read first | +| --------------------------------------- | ------------------------------------------------------ | +| Build failed | Build logs and `zerops.yaml` build steps | +| Runtime started then crashed | Runtime logs and start command | +| App is up but route fails | Runtime logs at request time and behavior check output | +| Local app cannot reach managed services | VPN state and generated `.env` values | ## Verify Verification has two layers: -| Layer | What it proves | -|---|---| +| Layer | What it proves | +| --------------------- | -------------------------------------------------------------------------------------------------------------- | | Platform reachability | The service is running, recent error logs are checked, and HTTP runtime services answer a probe when eligible. | -| Requested behavior | The thing you asked for works: endpoint body, UI state, database row, job result, or stage URL. | +| Requested behavior | The thing you asked for works: endpoint body, UI state, database row, job result, or stage URL. | Both layers must pass before the agent reports completion. +Good final proof sounds specific: + +```text +Deployed appdev, opened https://task-board.zerops.app, created a task, refreshed, and confirmed the task was still present. +``` + +Weak proof sounds generic: + +```text +Build passed and the app is running. +``` + ## Fix or stop The agent should name the failure point in plain language, read fresh logs/events/check output, fix the indicated cause, and redeploy. A repeated retry with no new evidence is not progress. -Use this cadence as the expected shape: +Use this escalation shape: -| Attempt | What should change | -|---|---| -| 1-2 | Diagnose from the named logs/events/check output and fix the targeted issue. | -| 3-4 | Re-check `zerops.yaml`, env vars, `deployFiles`, build commands, start command, and health/public route assumptions. | -| 5 | Stop and ask. Five attempts without progress means the loop needs a missing decision, credential, or human diagnosis. | +| Stage | What should change | +| ---------------- | -------------------------------------------------------------------------------------------------------------------- | +| First retries | Diagnose from the named logs/events/check output and fix the targeted issue. | +| Repeated failure | Re-check `zerops.yaml`, env vars, `deployFiles`, build commands, start command, and health/public route assumptions. | +| No progress | Stop and ask. The loop likely needs a missing decision, credential, or human diagnosis. | Stop sooner when scope is ambiguous, credentials are missing, destructive recovery is needed, or several evidence-based retries hit the same failure. ## Review and take over -The platform records deploys, events, runtime logs, and lifecycle actions. It does not record every agent thought, browser click, or shell edit. When taking over, ask for ZCP status first, then read service-scoped events, logs, deploy/verify results, and git history if delivery uses git-push. +The platform records deploys, events, runtime logs, and lifecycle actions. It does not record every agent thought, browser click, or shell edit. When taking over, ask for ZCP status first, then read service-scoped events, logs, deploy/verify results, and git history if delivery uses git. ## Gotchas diff --git a/apps/docs/content/zcp/workflows/build-with-zcp.mdx b/apps/docs/content/zcp/workflows/build-with-zcp.mdx index de036b062..d67e2f361 100644 --- a/apps/docs/content/zcp/workflows/build-with-zcp.mdx +++ b/apps/docs/content/zcp/workflows/build-with-zcp.mdx @@ -1,6 +1,6 @@ --- -title: "Build with ZCP" -description: "How an outcome prompt becomes service setup, code, deploy, verification, fixes, and delivery." +title: 'Build with ZCP' +description: 'How a product prompt turns into service setup, app work, verification, and delivery after proof.' --- Start with what the app should do, not with tool calls or workflow names. @@ -9,7 +9,7 @@ Start with what the app should do, not with tool calls or workflow names. Build a task board where tasks stay saved after refresh. ``` -Behind that prompt, the agent should inspect the project, choose a target runtime, make the app change, and use real deploy evidence until the requested behavior is proven. ZCP supplies the project knowledge and tool access needed for that loop: live services and env state, Zerops guidance, deploys, logs, verification, and guarded infrastructure changes. +Behind that prompt, the agent should inspect the project, choose the app runtime, make the change, deploy it, and use evidence from the real Zerops project until the requested behavior is proven. ZCP supplies the live service map, env state, deploy access, logs, events, verification checks, and guardrails needed for that loop. Do not make the prompt longer to restate completion expectations such as deploy, verification, or returning the URL. The words you add should change the app, the stack, the runtime layout, the acceptance criteria, or the delivery path. @@ -17,18 +17,22 @@ You also do not need to paste an infrastructure inventory, env wiring plan, or l ## The three parts of the work -The useful mental model is infrastructure, development, then delivery. ZCP makes those phases explicit enough that the agent can resume, recover, and stop at the right boundary without turning your prompt into an operations script. +The useful mental model is: -### 1. Infrastructure setup +1. **Infrastructure setup.** Find or create the runtime and dependencies the app needs. +2. **Development and verification.** Change the app, deploy it, and prove the requested behavior on Zerops. +3. **Delivery after proof.** Decide what happens to the finished work after a verified result exists. + +ZCP makes those boundaries explicit enough that the agent can resume, recover, and stop at the right point without turning your prompt into an operations script. -The agent first reads what exists now: runtime services, managed services, env vars, logs, recent events, and current work state. +### 1. Infrastructure setup -Then it establishes the app target: +The agent first reads what exists now: runtime services, managed services, env vars, recent events, and current work state. Then it establishes the app target: - use existing runtimes and managed services when they fit, - create missing runtimes or managed dependencies when the request needs them, - identify the app runtime that will receive code, -- keep `zcp` as the workspace/control-plane service, not the app runtime. +- keep `zcp` as the workspace/control-plane service, not as the app runtime. This phase ends when the app runtime and dependencies are known. It should not end with "the app is done"; no application behavior has been proven yet. @@ -48,31 +52,31 @@ After the target is known, the agent changes app files and `zerops.yaml`, deploy Two checks matter: -| Check | What it answers | -|---|---| -| Reachability | Did the runtime build, start, and answer on the real platform path? | +| Check | What it answers | +| ------------------ | -------------------------------------------------------------------------------------------------- | +| Reachability | Did the runtime build, start, and answer on the real platform path? | | Requested behavior | Does the app do the thing you asked for on the real URL, endpoint, worker result, or stored state? | A successful build is not enough. A page returning 200 is not enough when the prompt asked for saved tasks. The agent should report the behavior it checked, not only the deploy it ran. -Correction signals: +If the agent appears to drift, correct it in terms of the outcome you need: -| If the agent... | Ask it to... | -|---|---| -| writes files before naming the runtime target | read ZCP status and identify the app runtime first | -| targets the `zcp` service as the app | switch to the runtime service that runs app code | -| repeats deploys without new evidence | read fresh logs, events, or verify output and name the failure point | -| reports completion after only a green build | verify the requested behavior on the real URL or endpoint | +| If the agent... | Ask it to... | +| --------------------------------------------- | -------------------------------------------------------------------- | +| writes files before naming the runtime target | read ZCP status and identify the app runtime first | +| targets the `zcp` service as the app | switch to the runtime service that runs app code | +| repeats deploys without new evidence | read fresh logs, events, or verify output and name the failure point | +| reports completion after only a green build | verify the requested behavior on the real URL or endpoint | ### 3. Delivery after proof -After a verified deploy exists, choose what should happen to future changes: +After a verified deploy exists, choose what should happen to the finished work and future changes: -| Choice | Use when | -|---|---| +| Choice | Use when | +| ------------------ | ------------------------------------------------------------------------ | | Keep direct deploy | Fast iteration, demos, early development, or agent-owned dev/stage work. | -| Push to git | You want commits, repository history, review, or repo-driven builds. | -| External handoff | CI, release process, or a human owns delivery from here. | +| Push to git | You want commits, repository history, review, or repo-driven builds. | +| External handoff | CI, release process, or a human owns delivery from here. | You can include this intent in the original prompt, but it applies after proof: @@ -80,9 +84,9 @@ You can include this intent in the original prompt, but it applies after proof: Build a task board where tasks stay saved after refresh. After it verifies, push the result to git@github.com:my-org/task-dashboard.git. ``` -The first functional deploy still happens directly through ZCP. Delivery choice controls what follows a verified result. +The first functional deploy still happens through ZCP so the agent can prove the app in the target project. Delivery choice controls what follows a verified result. -Advanced export/reuse path: [Package a running service](/zcp/workflows/package-running-service). +Advanced export and reuse path: [Package a running service](/zcp/workflows/package-running-service). ## What the final answer should contain diff --git a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx index e1dc9125b..a5f97b8b0 100644 --- a/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx +++ b/apps/docs/content/zcp/workflows/create-or-adopt-services.mdx @@ -1,36 +1,36 @@ --- -title: "Service setup behind the prompt" -description: "How ZCP helps the agent use existing app services or create missing runtimes and managed dependencies before code work starts." +title: 'Set up app services' +description: 'How ZCP helps the agent reuse existing app services or create missing runtimes and managed dependencies before code work starts.' --- -When an app prompt needs infrastructure, ZCP should discover what already exists before creating runtimes or managed services. This usually happens because you asked for a product outcome, not because you explicitly asked for infrastructure. +When an app prompt needs infrastructure, ZCP should discover what already exists before creating anything. This usually happens because you asked for a product outcome, not because you explicitly asked for runtimes, databases, queues, or caches. For example, `Build a task board where tasks stay saved after refresh.` may require an app runtime and database. `Use the existing Laravel services` tells the agent to reuse what is already there instead of creating duplicates. -Behind that prompt, the agent should answer: +Behind that prompt, the agent should answer four practical questions: - Which runtime service will hold app code? - Which managed services does the app depend on? - Do those services already exist, or should the agent create them? - Are they running and visible to ZCP? -This part does **not** write application code, create `zerops.yaml`, run the first deploy, or verify behavior. Those belong to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). +This part is infrastructure setup. App files, `zerops.yaml`, deploys, and behavior checks belong to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). ## Starting situations -| Situation | What should happen | -|---|---| -| Runtime services already exist | Use them. Identify runtimes and managed dependencies. Do not recreate or rename services just to begin. | -| Project has only the `zcp` service | Treat it as empty from the app point of view. Create or import the requested service layout. | +| Situation | What should happen | +| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | +| Runtime services already exist | Use them when they fit. Identify runtimes and managed dependencies before changing code. | +| Project has only the `zcp` service | Treat it as empty from the app point of view. Create or import the requested service layout. | | Project is empty and request matches a known stack | A recipe may be a good starting base, but the starter still has to be changed into the requested app. | -| Project is empty and request needs a custom layout | Create a custom service plan before app work starts. | -| Setup was interrupted | Resume from current ZCP status instead of starting over. | +| Project is empty and request needs a custom layout | Create a custom service plan before app work starts. | +| Setup was interrupted | Resume from current ZCP status instead of starting over. | -The `zcp` service is the ZCP setup, not the application runtime target. +The `zcp` service is the ZCP workspace and control plane. It is not the application runtime target. ## Prompt examples -Product prompts can imply the service setup; they do not need to spell it out: +Product prompts can imply service setup. Add service details only when they change what should be built or reused: ```text Build a Node.js API with PostgreSQL. Use the existing dev+stage pair if this project has one. @@ -44,19 +44,24 @@ Use the existing Laravel services and build a small notes app. Add saved tasks to this task board. ``` -The agent should explain whether it used existing services or created new ones, then move into app work when infrastructure is known. +The agent should explain whether it used existing services or created new ones, then move into app work when the runtime and dependencies are known. ## Done state -Service setup is done when the app runtime and managed dependencies are known, any new services are visible, and the agent is ready to start app code, `zerops.yaml`, deploy, and verification work. +Service setup is done when: + +- the app runtime is identified, +- required managed services are identified, +- any new services are visible to ZCP, +- the agent can start app code, `zerops.yaml`, deploy, and verification work. When files, framework setup, deploys, logs, or behavior checks appear, the work has moved to [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app). -## Failure signals +## Correction signals -| Symptom | What to do | -|---|---| -| Agent targets `zcp` as the app | Correct the target to the runtime service. | -| Agent recreates existing runtimes | Ask it to inspect current services and use what exists. | +| Symptom | What to do | +| ------------------------------------------- | --------------------------------------------------------------------- | +| Agent targets `zcp` as the app | Correct the target to the runtime service. | +| Agent recreates existing runtimes | Ask it to inspect current services and use what exists. | | Agent writes app files during service setup | Stop and finish infrastructure first, or move explicitly to app work. | -| Service creation fails | Treat it as a hard stop until the failure is understood. | +| Service creation fails | Treat it as a hard stop until the failure is understood. | diff --git a/apps/docs/content/zcp/workflows/delivery-handoff.mdx b/apps/docs/content/zcp/workflows/delivery-handoff.mdx index f0560f441..6fe011f4a 100644 --- a/apps/docs/content/zcp/workflows/delivery-handoff.mdx +++ b/apps/docs/content/zcp/workflows/delivery-handoff.mdx @@ -1,21 +1,21 @@ --- -title: "Choose how finished work ships" -description: "After a verified deploy, choose whether future changes use direct deploy, git-push, or an external handoff." +title: 'Choose how finished work ships' +description: 'After a verified deploy, choose whether future changes use direct deploy, git-push, or an external handoff.' --- -Delivery is decided after the app works. You can include delivery intent in the original product prompt, but ZCP applies it only after a verified deploy exists. The first deploy uses ZCP's direct deploy path; the delivery choice records how future changes should ship. +Delivery is decided after the app works. You can include delivery intent in the original product prompt, but ZCP applies it only after a verified deploy exists. The first deploy uses ZCP's direct deploy path; the delivery choice records what should happen to the finished work and future changes. -If you do not have a CI or repository-driven release process yet, keep direct deploy for now. Git-push and external handoff become useful when source control, review, or a separate release owner matters. +If you do not have a repository-driven release process yet, keep direct deploy for now. Git-push and external handoff become useful when source control, review, CI, or a separate release owner matters. ZCP is for development and staging projects. Production should receive promoted work through your CI or release process; see [Production boundary](/zcp/security/production-policy). ## Three delivery choices -| Choice | Exact mode | Use when | What ZCP does next | -|---|---|---|---| -| Keep direct deploy | `auto` | Fast iteration, solo projects, demos, agent-owned dev/stage work. | Future changes keep deploying through ZCP directly. | -| Push to git | `git-push` | You want commits, review, repository history, or repo-driven builds. | The agent commits and pushes to a configured remote, then observes/verifies the resulting build when one is tracked. | -| External handoff | `manual` | CI, release process, or a human owns delivery from here. | ZCP records evidence but does not initiate the next deploy on its own. | +| Choice | Use when | What ZCP does next | +| ------------------ | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| Keep direct deploy | Fast iteration, solo projects, demos, agent-owned dev/stage work. | Future changes keep deploying through ZCP directly. | +| Push to git | You want commits, review, repository history, or repo-driven builds. | The agent commits and pushes to a configured remote, then observes and verifies the resulting build when one is tracked. | +| External handoff | CI, release process, or a human owns delivery from here. | ZCP records evidence but does not initiate the next deploy on its own. | You can ask in plain language: @@ -31,6 +31,14 @@ Keep ZCP deploying directly for now. My CI takes over after this verified deploy. ``` +If you see internal mode labels in agent output or reference pages, they map to the same three choices: + +| User choice | Internal label | +| ------------------ | -------------- | +| Keep direct deploy | `auto` | +| Push to git | `git-push` | +| External handoff | `manual` | + ## Direct deploy Direct deploy is the path ZCP already used to produce the first verified result. No extra setup is needed. @@ -45,7 +53,7 @@ Git-push needs committed code, a remote URL, and credentials that can push. It r Remote and local setup differ: -- Remote setup uses credentials configured inside the `zcp` service, commonly a fine-grained `GIT_TOKEN`. +- Remote setup uses credentials configured inside the `zcp` service, commonly a fine-grained token exposed as `GIT_TOKEN`. - Local setup uses your local git working directory and git credential helper. Local setup does not initialize git for you. If the directory is not a git repository yet, initialize it and make a first commit before asking for git-push delivery. @@ -56,11 +64,11 @@ Remote setup can prepare repository state during service setup, but git-push sti Git-push capability and build integration are separate. A push may trigger one of three outcomes: -| Outcome | Meaning | -|---|---| -| Nothing tracked by ZCP | Your repository may still have its own hooks or CI, but ZCP did not set up or observe one. | +| Outcome | Meaning | +| ---------------------------------- | ----------------------------------------------------------------------------------------------- | +| Nothing tracked by ZCP | Your repository may still have its own hooks or CI, but ZCP did not set up or observe one. | | Zerops dashboard build integration | Zerops pulls from the repository and runs the build/deploy pipeline from dashboard integration. | -| GitHub Actions workflow | A workflow checks out the repo and deploys to Zerops with `zcli`. | +| GitHub Actions workflow | A workflow checks out the repo and deploys to Zerops with `zcli`. | The GitHub Actions path needs a Zerops API token stored as a repository secret named `ZEROPS_TOKEN`. It is not a GitHub token. The project-scoped token already used by `ZCP_API_KEY` can be reused if your team wants one rotation surface. @@ -68,7 +76,7 @@ A push is not proof that the app works. If a webhook or GitHub Actions build run ## External handoff -External handoff means a human or external system owns delivery after the verified ZCP result. ZCP can still read state, verify, and record deploy evidence when asked, but it does not auto-initiate future deploys as the close condition. +External handoff means a human or external system owns delivery after the verified ZCP result. ZCP can still read state, verify, and record deploy evidence when asked, but it does not initiate future deploys on its own. Pick this when the next step is a release review, a non-ZCP CI pipeline, a production promotion process, or a human decision that should not be hidden inside the agent loop. @@ -82,7 +90,7 @@ Git-push capability can also coexist with direct deploy. A configured remote doe ## Gotchas -1. **First deploy is always direct.** Setting up git-push early does not redirect the deploy that creates the first verified running result. +1. **First verified deploy is direct.** Setting up git-push early does not redirect the deploy that creates the first verified running result. 2. **Delivery choice is not a hidden deploy button.** It records the future path after the app already works. 3. **External handoff is not a deploy by itself.** It means a human or external system owns the next delivery action. 4. **GitHub Actions uses `ZEROPS_TOKEN`.** That secret is a Zerops API token, not your GitHub PAT. diff --git a/apps/docs/content/zcp/workflows/package-running-service.mdx b/apps/docs/content/zcp/workflows/package-running-service.mdx index 2e1992218..f1ab90acd 100644 --- a/apps/docs/content/zcp/workflows/package-running-service.mdx +++ b/apps/docs/content/zcp/workflows/package-running-service.mdx @@ -1,18 +1,18 @@ --- -title: "Package a running service" -description: "Use ZCP to turn a running Zerops service into a re-importable bundle." +title: 'Package a running service' +description: 'Use ZCP to turn a running Zerops service into a re-importable bundle.' --- -Use packaging when a deployed runtime should become a reusable import bundle. This is an advanced handoff or reuse workflow, not the normal way to deploy the next change. The bundle's `buildFromGit:` URL points at the same source repo, so re-importing creates a fresh project that builds itself from git. +Use packaging when a deployed runtime should become a reusable import bundle. This is an advanced handoff or reuse workflow, not the normal way to deploy the next app change. -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 layout into a fresh Zerops project. +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. The import file points back to the same source repo through `buildFromGit:`, so a fresh Zerops project can rebuild the app from git instead of carrying source code inside YAML. ## 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 a re-importable snapshot you can paste into a fresh project later. - 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: @@ -20,13 +20,13 @@ Packaging is **not** the right tool when: - You want the next app change deployed. For that, ask for the app result; direct deploy is the expected ZCP path behind the request. - You want to deploy the next change to an existing production project. Keep production in its own Zerops project and operate it through Zerops UI, the API, or CI. -Packaging does not keep a long-running session. If interrupted, the agent reads live state and re-runs the export flow; you only decide when the dev or stage runtime, or env classification, is ambiguous. +Packaging does not keep a long-running session. If interrupted, the agent reads live state and prepares the bundle again. You only need to decide when the runtime choice or env-var classification is ambiguous. ## 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. +Packaging captures **one** runtime. If your project has a dev runtime and a separate stage runtime, the agent asks which one to package. They may carry different env values, start commands, or `setup:` blocks. For single-runtime projects, the choice is implied. -Managed services (`db`, `redis`, etc.) come along automatically as dependencies — you don't pick them. +Managed services (`db`, `redis`, etc.) come along automatically as dependencies; you do not pick them one by one. A typical first prompt: @@ -34,20 +34,20 @@ A typical first prompt: 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. +The agent handles the packaging work; you only step in if it asks which runtime to package or how to classify a value. ## Classify env vars (secrets, project-scoped values, public values) -Once the runtime is chosen, 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: +Once the runtime is chosen, ZCP prepares 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. | +| 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 does not guess these buckets from key names alone. The agent should classify them from source code: if the value is read by a Stripe SDK call it is `external-secret`; if it appears as `${db_*}` from a managed service it is `infrastructure`. The packaging response carries env *keys* and redacts values; the agent fetches values only when it needs them. +ZCP does not guess these buckets from key names alone. The agent should classify them from source code: if the value is read by a Stripe SDK call it is `external-secret`; if it appears as `${db_*}` from a managed service it is `infrastructure`. The packaging response carries env _keys_ and redacts values; the agent fetches values only 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: @@ -57,7 +57,7 @@ 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. +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 @@ -65,7 +65,7 @@ A successful run produces a **single-repo, self-contained bundle** with two file ```yaml #zeropsPreprocessor=on -# zerops-project-import.yaml — paste into a fresh Zerops project +# zerops-project-import.yaml - paste into a fresh Zerops project project: name: demo envVariables: @@ -89,7 +89,7 @@ services: ``` ```yaml -# zerops.yaml — verbatim copy from the running runtime +# zerops.yaml - verbatim copy from the running runtime zerops: - setup: appdev build: @@ -113,20 +113,20 @@ zerops: A few things worth noticing: -- The first line is the [Zerops preprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `<@...>` directives (here `<@generateRandomString(<32>)>` for `APP_KEY`), `#zeropsPreprocessor=on` must be 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. +- The first line is the [Zerops preprocessor](/references/import-yaml/pre-processor) header. When the bundle carries `<@...>` directives (here `<@generateRandomString(<32>)>` for `APP_KEY`), `#zeropsPreprocessor=on` must be line 1. Without it, the platform skips directive expansion at re-import and the literal string lands in the env var. ZCP adds this 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). Packaging can be part of a handoff intent. The agent should produce the files, place them where the chosen handoff path expects them, and commit or push when that delivery choice is configured. Packaging itself produces the bundle; repository changes still happen through the normal [handoff path](/zcp/workflows/delivery-handoff). ## 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 | +| 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 @@ -136,6 +136,6 @@ Packaging can be part of a handoff intent. The agent should produce the files, p ## Next steps -- [Choose how finished work ships](/zcp/workflows/delivery-handoff) — commit and push the bundle's two files. -- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) — the verified runtime this packages. -- [How ZCP works](/zcp/concept/how-it-works) — why packaging stays stateless. +- [Choose how finished work ships](/zcp/workflows/delivery-handoff) - commit and push the bundle's two files. +- [Deploy, verify, and fix](/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/sidebars.js b/apps/docs/sidebars.js index 2d771d4a6..3aaa3a7a9 100644 --- a/apps/docs/sidebars.js +++ b/apps/docs/sidebars.js @@ -158,23 +158,23 @@ module.exports = { }, className: 'homepage-sidebar-item', }, -// { -// type: 'html', -// value: 'Perfectly suited for', -// customProps: { -// sidebar_is_group_divider: true, -// }, -// className: 'homepage-sidebar-item', -// }, -// { -// type: 'ref', -// id: 'frameworks/laravel', -// label: 'Laravel', -// customProps: { -// sidebar_icon: 'laravel', -// }, -// className: 'homepage-sidebar-item service-sidebar-item', -// }, + // { + // type: 'html', + // value: 'Perfectly suited for', + // customProps: { + // sidebar_is_group_divider: true, + // }, + // className: 'homepage-sidebar-item', + // }, + // { + // type: 'ref', + // id: 'frameworks/laravel', + // label: 'Laravel', + // customProps: { + // sidebar_icon: 'laravel', + // }, + // className: 'homepage-sidebar-item service-sidebar-item', + // }, { type: 'html', value: 'All Supported Services', @@ -308,7 +308,6 @@ module.exports = { }, className: 'homepage-sidebar-item service-sidebar-item', }, - ], }, { @@ -375,76 +374,76 @@ module.exports = { className: 'homepage-sidebar-item service-sidebar-item', }, { - type: "ref", - id: "valkey/overview", - label: "Valkey (Redis)", + type: 'ref', + id: 'valkey/overview', + label: 'Valkey (Redis)', customProps: { - sidebar_icon: "valkey", + sidebar_icon: 'valkey', }, - className: "homepage-sidebar-item service-sidebar-item", + className: 'homepage-sidebar-item service-sidebar-item', }, { - type: "ref", - id: "elasticsearch/overview", - label: "Elasticsearch", + type: 'ref', + id: 'elasticsearch/overview', + label: 'Elasticsearch', customProps: { - sidebar_icon: "elasticsearch", + sidebar_icon: 'elasticsearch', }, - className: "homepage-sidebar-item service-sidebar-item", + className: 'homepage-sidebar-item service-sidebar-item', }, { - type: "ref", - id: "typesense/overview", - label: "Typesense", + type: 'ref', + id: 'typesense/overview', + label: 'Typesense', customProps: { - sidebar_icon: "typesense", + sidebar_icon: 'typesense', }, - className: "homepage-sidebar-item service-sidebar-item", + className: 'homepage-sidebar-item service-sidebar-item', }, { - type: "ref", - id: "meilisearch/overview", - label: "Meilisearch", + type: 'ref', + id: 'meilisearch/overview', + label: 'Meilisearch', customProps: { - sidebar_icon: "meilisearch", + sidebar_icon: 'meilisearch', }, - className: "homepage-sidebar-item service-sidebar-item", + className: 'homepage-sidebar-item service-sidebar-item', }, { - type: "ref", - id: "qdrant/overview", - label: "Qdrant", + type: 'ref', + id: 'qdrant/overview', + label: 'Qdrant', customProps: { - sidebar_icon: "qdrant", + sidebar_icon: 'qdrant', }, - className: "homepage-sidebar-item service-sidebar-item", + className: 'homepage-sidebar-item service-sidebar-item', }, { - type: "ref", - id: "nats/overview", - label: "NATS", + type: 'ref', + id: 'nats/overview', + label: 'NATS', customProps: { - sidebar_icon: "nats", + sidebar_icon: 'nats', }, - className: "homepage-sidebar-item service-sidebar-item", + className: 'homepage-sidebar-item service-sidebar-item', }, { - type: "ref", - id: "kafka/overview", - label: "Kafka", + type: 'ref', + id: 'kafka/overview', + label: 'Kafka', customProps: { - sidebar_icon: "kafka", + sidebar_icon: 'kafka', }, - className: "homepage-sidebar-item service-sidebar-item", + className: 'homepage-sidebar-item service-sidebar-item', }, { - type: "ref", - id: "clickhouse/overview", - label: "Clickhouse", + type: 'ref', + id: 'clickhouse/overview', + label: 'Clickhouse', customProps: { - sidebar_icon: "clickhouse", + sidebar_icon: 'clickhouse', }, - className: "homepage-sidebar-item service-sidebar-item", + className: 'homepage-sidebar-item service-sidebar-item', }, { type: 'ref', @@ -575,7 +574,7 @@ module.exports = { }, className: 'homepage-sidebar-item', }, - ] + ], }, { type: 'category', @@ -627,7 +626,7 @@ module.exports = { { type: 'doc', id: 'zcp/setup/hosted-workspace', - label: 'Set up remote ZCP', + label: 'Use remote ZCP workspace', }, { type: 'doc', @@ -819,7 +818,7 @@ module.exports = { }, className: 'homepage-sidebar-item', }, - ] + ], }, { type: 'category', @@ -895,7 +894,7 @@ module.exports = { }, className: 'homepage-sidebar-item', }, - ] + ], }, { type: 'category', @@ -932,7 +931,7 @@ module.exports = { }, className: 'homepage-sidebar-item', }, - ] + ], }, { type: 'html', @@ -1133,15 +1132,15 @@ module.exports = { }, ], }, - { - type: 'doc', - id: 'nodejs/faq', - label: 'FAQ', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'chat-bubble-left-right', - }, + { + type: 'doc', + id: 'nodejs/faq', + label: 'FAQ', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'chat-bubble-left-right', }, + }, ], php: [ { @@ -1399,640 +1398,6 @@ module.exports = { }, ], go: [ - { - type: 'ref', - id: 'homepage', - label: 'Back to home', - customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', - }, - }, - { - type: 'doc', - id: 'go/overview', - label: 'Go', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'go', - }, - }, - { - type: 'category', - label: 'Management', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'go/how-to/create', - label: 'Create Go service', - }, - { - type: 'doc', - id: 'go/how-to/upgrade', - label: 'Upgrade Go service', - }, - { - type: 'doc', - id: 'go/how-to/controls', - label: 'Stop, start & delete Go runtime service', - }, - ], - }, - { - type: 'category', - label: 'Configuration & Environment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'go/how-to/env-variables', - label: 'Manage environment variables', - }, - { - type: 'doc', - id: 'go/how-to/customize-runtime', - label: 'Customize Go runtime', - }, - { - type: 'doc', - id: 'go/how-to/scaling', - label: 'Scale Go runtime service', - }, - ], - }, - { - type: 'category', - label: 'Build & Deployment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'go/how-to/build-pipeline', - label: 'Configure build & deploy pipeline', - }, - { - type: 'doc', - id: 'go/how-to/trigger-pipeline', - label: 'Trigger build pipeline', - }, - { - type: 'doc', - id: 'go/how-to/build-process', - label: 'Build process', - }, - { - type: 'doc', - id: 'go/how-to/deploy-process', - label: 'Deploy process', - }, - ], - }, - { - type: 'category', - label: 'Maintenance & Monitoring', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'go/how-to/logs', - label: 'Setup & access logs', - }, - { - type: 'doc', - id: 'go/how-to/filebrowser', - label: 'Browse container files', - }, - { - type: 'doc', - id: 'go/how-to/shared-storage', - label: 'Connect / disconnect shared storage', - }, - ], - }, - ], - rust: [ - { - type: 'ref', - id: 'homepage', - label: 'Back to home', - customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', - }, - }, - { - type: 'doc', - id: 'rust/overview', - label: 'Rust', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'rust', - }, - }, - { - type: 'category', - label: 'Management', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'rust/how-to/create', - label: 'Create Rust service', - }, - { - type: 'doc', - id: 'rust/how-to/upgrade', - label: 'Upgrade Rust service', - }, - { - type: 'doc', - id: 'rust/how-to/controls', - label: 'Stop, start & delete Rust runtime service', - }, - ], - }, - { - type: 'category', - label: 'Configuration & Environment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'rust/how-to/env-variables', - label: 'Manage environment variables', - }, - { - type: 'doc', - id: 'rust/how-to/customize-runtime', - label: 'Customize Rust runtime', - }, - { - type: 'doc', - id: 'rust/how-to/scaling', - label: 'Scale Rust runtime service', - }, - ], - }, - { - type: 'category', - label: 'Build & Deployment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'rust/how-to/build-pipeline', - label: 'Configure build & deploy pipeline', - }, - { - type: 'doc', - id: 'rust/how-to/trigger-pipeline', - label: 'Trigger build pipeline', - }, - { - type: 'doc', - id: 'rust/how-to/build-process', - label: 'Build process', - }, - { - type: 'doc', - id: 'rust/how-to/deploy-process', - label: 'Deploy process', - }, - ], - }, - { - type: 'category', - label: 'Maintenance & Monitoring', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'rust/how-to/logs', - label: 'Setup & access logs', - }, - { - type: 'doc', - id: 'rust/how-to/filebrowser', - label: 'Browse container files', - }, - { - type: 'doc', - id: 'rust/how-to/shared-storage', - label: 'Connect / disconnect shared storage', - }, - ], - }, - ], - dotnet: [ - { - type: 'ref', - id: 'homepage', - label: 'Back to home', - customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', - }, - }, - { - type: 'doc', - id: 'dotnet/overview', - label: '.NET', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'dotnet', - }, - }, - { - type: 'category', - label: 'Management', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'dotnet/how-to/create', - label: 'Create .NET service', - }, - { - type: 'doc', - id: 'dotnet/how-to/upgrade', - label: 'Upgrade .NET service', - }, - { - type: 'doc', - id: 'dotnet/how-to/controls', - label: 'Stop, start & delete .NET runtime service', - }, - ], - }, - { - type: 'category', - label: 'Configuration & Environment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'dotnet/how-to/env-variables', - label: 'Manage environment variables', - }, - { - type: 'doc', - id: 'dotnet/how-to/customize-runtime', - label: 'Customize .NET runtime', - }, - { - type: 'doc', - id: 'dotnet/how-to/scaling', - label: 'Scale .NET runtime service', - }, - ], - }, - { - type: 'category', - label: 'Build & Deployment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'dotnet/how-to/build-pipeline', - label: 'Configure build & deploy pipeline', - }, - { - type: 'doc', - id: 'dotnet/how-to/trigger-pipeline', - label: 'Trigger build pipeline', - }, - { - type: 'doc', - id: 'dotnet/how-to/build-process', - label: 'Build process', - }, - { - type: 'doc', - id: 'dotnet/how-to/deploy-process', - label: 'Deploy process', - }, - ], - }, - { - type: 'category', - label: 'Maintenance & Monitoring', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'dotnet/how-to/logs', - label: 'Setup & access logs', - }, - { - type: 'doc', - id: 'dotnet/how-to/filebrowser', - label: 'Browse container files', - }, - { - type: 'doc', - id: 'dotnet/how-to/shared-storage', - label: 'Connect / disconnect shared storage', - }, - ], - }, - ], - java: [ - { - type: 'ref', - id: 'homepage', - label: 'Back to home', - customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', - }, - }, - { - type: 'doc', - id: 'java/overview', - label: 'Java', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'java', - }, - }, - { - type: 'category', - label: 'Management', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'java/how-to/create', - label: 'Create Java service', - }, - { - type: 'doc', - id: 'java/how-to/upgrade', - label: 'Upgrade Java service', - }, - { - type: 'doc', - id: 'java/how-to/controls', - label: 'Stop, start & delete Java runtime service', - }, - ], - }, - { - type: 'category', - label: 'Configuration & Environment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'java/how-to/env-variables', - label: 'Manage environment variables', - }, - { - type: 'doc', - id: 'java/how-to/customize-runtime', - label: 'Customize Java runtime', - }, - { - type: 'doc', - id: 'java/how-to/scaling', - label: 'Scale Java runtime service', - }, - ], - }, - { - type: 'category', - label: 'Build & Deployment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'java/how-to/build-pipeline', - label: 'Configure build & deploy pipeline', - }, - { - type: 'doc', - id: 'java/how-to/trigger-pipeline', - label: 'Trigger build pipeline', - }, - { - type: 'doc', - id: 'java/how-to/build-process', - label: 'Build process', - }, - { - type: 'doc', - id: 'java/how-to/deploy-process', - label: 'Deploy process', - }, - ], - }, - { - type: 'category', - label: 'Maintenance & Monitoring', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'java/how-to/logs', - label: 'Setup & access logs', - }, - { - type: 'doc', - id: 'java/how-to/filebrowser', - label: 'Browse container files', - }, - { - type: 'doc', - id: 'java/how-to/shared-storage', - label: 'Connect / disconnect shared storage', - }, - ], - }, - ], - nginx: [ - { - type: 'ref', - id: 'homepage', - label: 'Back to home', - customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', - }, - }, - { - type: 'doc', - id: 'nginx/overview', - label: 'Nginx Static', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'nginx', - }, - }, - { - type: 'category', - label: 'Management', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'nginx/how-to/create', - label: 'Create Nginx static service', - }, - { - type: 'doc', - id: 'nginx/how-to/upgrade', - label: 'Upgrade Nginx service', - }, - { - type: 'doc', - id: 'nginx/how-to/controls', - label: 'Stop, start & delete Nginx static service', - }, - ], - }, - { - type: 'category', - label: 'Configuration & Environment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'nginx/how-to/env-variables', - label: 'Manage environment variables', - }, - { - type: 'doc', - id: 'nginx/how-to/customize-runtime', - label: 'Customize Nginx static runtime', - }, - { - type: 'doc', - id: 'nginx/how-to/customize-web-server', - label: 'Customize web server', - }, - { - type: 'doc', - id: 'nginx/how-to/scaling', - label: 'Scale Nginx static service', - }, - ], - }, - { - type: 'category', - label: 'Build & Deployment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'nginx/how-to/build-pipeline', - label: 'Configure build & deploy pipeline', - }, - { - type: 'doc', - id: 'nginx/how-to/trigger-pipeline', - label: 'Trigger build pipeline', - }, - { - type: 'doc', - id: 'nginx/how-to/deploy-process', - label: 'Deploy process', - }, - ], - }, - { - type: 'category', - label: 'Maintenance & Monitoring', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'nginx/how-to/logs', - label: 'Setup & access logs', - }, - { - type: 'doc', - id: 'nginx/how-to/filebrowser', - label: 'Browse container files', - }, - { - type: 'doc', - id: 'nginx/how-to/shared-storage', - label: 'Connect / disconnect shared storage', - }, - ], - }, - { - type: 'doc', - id: 'nginx/faq', - label: 'FAQ', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'chat-bubble-left-right', - }, - }, - ], - static: [ { type: 'ref', id: 'homepage', @@ -2044,285 +1409,120 @@ module.exports = { }, { type: 'doc', - id: 'static/overview', - label: 'Static Service', + id: 'go/overview', + label: 'Go', customProps: { sidebar_is_title: true, - sidebar_icon: 'computer-desktop', + sidebar_icon: 'go', }, }, - ], - ubuntu: [ + { + type: 'category', + label: 'Management', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ { - type: 'ref', - id: 'homepage', - label: 'Back to home', - customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', - }, + type: 'doc', + id: 'go/how-to/create', + label: 'Create Go service', }, { type: 'doc', - id: 'ubuntu/overview', - label: 'Ubuntu', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'ubuntu', - }, + id: 'go/how-to/upgrade', + label: 'Upgrade Go service', }, { - type: 'category', - label: 'Management', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'ubuntu/how-to/create', - label: 'Create Ubuntu service', - }, - { - type: 'doc', - id: 'ubuntu/how-to/upgrade', - label: 'Upgrade Ubuntu service', - }, - { - type: 'doc', - id: 'ubuntu/how-to/controls', - label: 'Stop, start & delete Ubuntu runtime service', - }, - ], + type: 'doc', + id: 'go/how-to/controls', + label: 'Stop, start & delete Go runtime service', }, + ], + }, + { + type: 'category', + label: 'Configuration & Environment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ { - type: 'category', - label: 'Configuration & Environment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'ubuntu/how-to/env-variables', - label: 'Manage environment variables', - }, - { - type: 'doc', - id: 'ubuntu/how-to/customize-runtime', - label: 'Customize Ubuntu runtime', - }, - { - type: 'doc', - id: 'ubuntu/how-to/scaling', - label: 'Scale Ubuntu runtime service', - }, - ], + type: 'doc', + id: 'go/how-to/env-variables', + label: 'Manage environment variables', }, { - type: 'category', - label: 'Build & Deployment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'ubuntu/how-to/build-pipeline', - label: 'Configure build & deploy pipeline', - }, - { - type: 'doc', - id: 'ubuntu/how-to/trigger-pipeline', - label: 'Trigger build pipeline', - }, - { - type: 'doc', - id: 'ubuntu/how-to/build-process', - label: 'Build process', - }, - { - type: 'doc', - id: 'ubuntu/how-to/deploy-process', - label: 'Deploy process', - }, - ], + type: 'doc', + id: 'go/how-to/customize-runtime', + label: 'Customize Go runtime', }, { - type: 'category', - label: 'Maintenance & Monitoring', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'ubuntu/how-to/logs', - label: 'Setup & access logs', - }, - { - type: 'doc', - id: 'ubuntu/how-to/filebrowser', - label: 'Browse container files', - }, - { - type: 'doc', - id: 'ubuntu/how-to/shared-storage', - label: 'Connect / disconnect shared storage', - }, - ], + type: 'doc', + id: 'go/how-to/scaling', + label: 'Scale Go runtime service', }, ], - alpine: [ - { - type: 'ref', - id: 'homepage', - label: 'Back to home', - customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', - }, - }, - { - type: 'doc', - id: 'alpine/overview', - label: 'Alpine', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'alpine', - }, - }, - { - type: 'category', - label: 'Management', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'alpine/how-to/create', - label: 'Create Alpine service', - }, - { - type: 'doc', - id: 'alpine/how-to/upgrade', - label: 'Upgrade Alpine service', - }, - { - type: 'doc', - id: 'alpine/how-to/controls', - label: 'Stop, start & delete Alpine runtime service', - }, - ], - }, - { - type: 'category', - label: 'Configuration & Environment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'alpine/how-to/env-variables', - label: 'Manage environment variables', - }, - { - type: 'doc', - id: 'alpine/how-to/customize-runtime', - label: 'Customize Alpine runtime', - }, - { - type: 'doc', - id: 'alpine/how-to/scaling', - label: 'Scale Alpine runtime service', - }, - ], - }, - { - type: 'category', - label: 'Build & Deployment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'alpine/how-to/build-pipeline', - label: 'Configure build & deploy pipeline', - }, - { - type: 'doc', - id: 'alpine/how-to/trigger-pipeline', - label: 'Trigger build pipeline', - }, - { - type: 'doc', - id: 'alpine/how-to/build-process', - label: 'Build process', - }, - { - type: 'doc', - id: 'alpine/how-to/deploy-process', - label: 'Deploy process', - }, - ], - }, - { - type: 'category', - label: 'Maintenance & Monitoring', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'alpine/how-to/logs', - label: 'Setup & access logs', - }, - { - type: 'doc', - id: 'alpine/how-to/filebrowser', - label: 'Browse container files', - }, - { - type: 'doc', - id: 'alpine/how-to/shared-storage', - label: 'Connect / disconnect shared storage', - }, - ], - }, - ], - docker: [ + }, { - type: 'ref', - id: 'homepage', - label: 'Back to home', + type: 'category', + label: 'Build & Deployment', + collapsible: false, customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', + sidebar_is_group_headline: true, }, + items: [ + { + type: 'doc', + id: 'go/how-to/build-pipeline', + label: 'Configure build & deploy pipeline', + }, + { + type: 'doc', + id: 'go/how-to/trigger-pipeline', + label: 'Trigger build pipeline', + }, + { + type: 'doc', + id: 'go/how-to/build-process', + label: 'Build process', + }, + { + type: 'doc', + id: 'go/how-to/deploy-process', + label: 'Deploy process', + }, + ], }, { - type: 'doc', - id: 'docker/overview', - label: 'Docker Service', + type: 'category', + label: 'Maintenance & Monitoring', + collapsible: false, customProps: { - sidebar_is_title: true, - sidebar_icon: 'docker', + sidebar_is_group_headline: true, }, + items: [ + { + type: 'doc', + id: 'go/how-to/logs', + label: 'Setup & access logs', + }, + { + type: 'doc', + id: 'go/how-to/filebrowser', + label: 'Browse container files', + }, + { + type: 'doc', + id: 'go/how-to/shared-storage', + label: 'Connect / disconnect shared storage', + }, + ], }, ], - mariadb: [ + rust: [ { type: 'ref', id: 'homepage', @@ -2334,16 +1534,16 @@ module.exports = { }, { type: 'doc', - id: 'mariadb/overview', - label: 'Zerops MariaDB Service', + id: 'rust/overview', + label: 'Rust', customProps: { sidebar_is_title: true, - sidebar_icon: 'mariadb', + sidebar_icon: 'rust', }, }, { type: 'category', - label: 'How-to', + label: 'Management', collapsible: false, customProps: { sidebar_is_group_headline: true, @@ -2351,44 +1551,79 @@ module.exports = { items: [ { type: 'doc', - id: 'mariadb/how-to/create', - label: 'Create MariaDB service', + id: 'rust/how-to/create', + label: 'Create Rust service', }, { type: 'doc', - id: 'mariadb/how-to/connect', - label: 'Connect to MariaDB', + id: 'rust/how-to/upgrade', + label: 'Upgrade Rust service', }, { type: 'doc', - id: 'mariadb/how-to/manage', - label: 'Manage users and databases', + id: 'rust/how-to/controls', + label: 'Stop, start & delete Rust runtime service', + }, + ], + }, + { + type: 'category', + label: 'Configuration & Environment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'rust/how-to/env-variables', + label: 'Manage environment variables', }, { type: 'doc', - id: 'mariadb/how-to/export-import-data', - label: 'Export and import data', + id: 'rust/how-to/customize-runtime', + label: 'Customize Rust runtime', }, { type: 'doc', - id: 'mariadb/how-to/backup', - label: 'Backup & restore data', + id: 'rust/how-to/scaling', + label: 'Scale Rust runtime service', + }, + ], + }, + { + type: 'category', + label: 'Build & Deployment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'rust/how-to/build-pipeline', + label: 'Configure build & deploy pipeline', }, { type: 'doc', - id: 'mariadb/how-to/scale', - label: 'Scale MariaDB service', + id: 'rust/how-to/trigger-pipeline', + label: 'Trigger build pipeline', }, { type: 'doc', - id: 'mariadb/how-to/control', - label: 'Stop, start and delete MariaDB service', + id: 'rust/how-to/build-process', + label: 'Build process', + }, + { + type: 'doc', + id: 'rust/how-to/deploy-process', + label: 'Deploy process', }, ], }, { type: 'category', - label: 'Technical details', + label: 'Maintenance & Monitoring', collapsible: false, customProps: { sidebar_is_group_headline: true, @@ -2396,27 +1631,23 @@ module.exports = { items: [ { type: 'doc', - id: 'mariadb/tech-details/cluster', - label: 'MariaDB cluster asynchronous behaviour', + id: 'rust/how-to/logs', + label: 'Setup & access logs', }, { type: 'doc', - id: 'mariadb/tech-details/limitations', - label: 'Technical limitations of MariaDB cluster', + id: 'rust/how-to/filebrowser', + label: 'Browse container files', + }, + { + type: 'doc', + id: 'rust/how-to/shared-storage', + label: 'Connect / disconnect shared storage', }, ], }, - // { - // type: 'doc', - // id: 'mariadb/faq', - // label: 'FAQ', - // customProps: { - // sidebar_is_title: true, - // sidebar_icon: 'chat-bubble-left-right', - // }, - // }, ], - postgresql: [ + dotnet: [ { type: 'ref', id: 'homepage', @@ -2428,16 +1659,16 @@ module.exports = { }, { type: 'doc', - id: 'postgresql/overview', - label: 'Zerops PostgreSQL Service', + id: 'dotnet/overview', + label: '.NET', customProps: { sidebar_is_title: true, - sidebar_icon: 'postgresql', + sidebar_icon: 'dotnet', }, }, { type: 'category', - label: 'How-to', + label: 'Management', collapsible: false, customProps: { sidebar_is_group_headline: true, @@ -2445,72 +1676,103 @@ module.exports = { items: [ { type: 'doc', - id: 'postgresql/how-to/create', - label: 'Create PostgreSQL service', - }, - { - type: 'doc', - id: 'postgresql/how-to/connect', - label: 'Connect to PostgreSQL', + id: 'dotnet/how-to/create', + label: 'Create .NET service', }, { type: 'doc', - id: 'postgresql/how-to/manage', - label: 'Manage users, databases & plugins', + id: 'dotnet/how-to/upgrade', + label: 'Upgrade .NET service', }, { type: 'doc', - id: 'postgresql/how-to/export-import-data', - label: 'Export and import data', + id: 'dotnet/how-to/controls', + label: 'Stop, start & delete .NET runtime service', }, + ], + }, + { + type: 'category', + label: 'Configuration & Environment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ { type: 'doc', - id: 'postgresql/how-to/backup', - label: 'Backup data', + id: 'dotnet/how-to/env-variables', + label: 'Manage environment variables', }, { type: 'doc', - id: 'postgresql/how-to/scale', - label: 'Scale PostgreSQL service', + id: 'dotnet/how-to/customize-runtime', + label: 'Customize .NET runtime', }, { type: 'doc', - id: 'postgresql/how-to/control', - label: 'Stop, start and delete PostgreSQL service', + id: 'dotnet/how-to/scaling', + label: 'Scale .NET runtime service', }, ], }, - // { - // type: "category", - // label: "Technical details", - // collapsible: false, - // customProps: { - // sidebar_is_group_headline: true, - // }, - // items: [ - // { - // type: "doc", - // id: "postgresql/tech-details/cluster", - // label: "PostgreSQL cluster asynchronous behaviour", - // }, - // { - // type: "doc", - // id: "postgresql/tech-details/limitations", - // label: "Technical limitations of PostgreSQL cluster", - // }, - // ], - // }, { - type: 'doc', - id: 'postgresql/faq', - label: 'FAQ', + type: 'category', + label: 'Build & Deployment', + collapsible: false, customProps: { - sidebar_is_title: true, - sidebar_icon: 'chat-bubble-left-right', + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'dotnet/how-to/build-pipeline', + label: 'Configure build & deploy pipeline', + }, + { + type: 'doc', + id: 'dotnet/how-to/trigger-pipeline', + label: 'Trigger build pipeline', + }, + { + type: 'doc', + id: 'dotnet/how-to/build-process', + label: 'Build process', + }, + { + type: 'doc', + id: 'dotnet/how-to/deploy-process', + label: 'Deploy process', + }, + ], + }, + { + type: 'category', + label: 'Maintenance & Monitoring', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, }, + items: [ + { + type: 'doc', + id: 'dotnet/how-to/logs', + label: 'Setup & access logs', + }, + { + type: 'doc', + id: 'dotnet/how-to/filebrowser', + label: 'Browse container files', + }, + { + type: 'doc', + id: 'dotnet/how-to/shared-storage', + label: 'Connect / disconnect shared storage', + }, + ], }, ], - elasticsearch: [ + java: [ { type: 'ref', id: 'homepage', @@ -2522,36 +1784,66 @@ module.exports = { }, { type: 'doc', - id: 'elasticsearch/overview', - label: 'Zerops Elasticsearch Service', + id: 'java/overview', + label: 'Java', customProps: { sidebar_is_title: true, - sidebar_icon: 'elasticsearch', + sidebar_icon: 'java', }, }, - ], - keydb: [ { - type: 'ref', - id: 'homepage', - label: 'Back to home', + type: 'category', + label: 'Management', + collapsible: false, customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', + sidebar_is_group_headline: true, }, + items: [ + { + type: 'doc', + id: 'java/how-to/create', + label: 'Create Java service', + }, + { + type: 'doc', + id: 'java/how-to/upgrade', + label: 'Upgrade Java service', + }, + { + type: 'doc', + id: 'java/how-to/controls', + label: 'Stop, start & delete Java runtime service', + }, + ], }, { - type: 'doc', - id: 'keydb/overview', - label: 'Zerops KeyDB Service', + type: 'category', + label: 'Configuration & Environment', + collapsible: false, customProps: { - sidebar_is_title: true, - sidebar_icon: 'keydb', + sidebar_is_group_headline: true, }, + items: [ + { + type: 'doc', + id: 'java/how-to/env-variables', + label: 'Manage environment variables', + }, + { + type: 'doc', + id: 'java/how-to/customize-runtime', + label: 'Customize Java runtime', + }, + { + type: 'doc', + id: 'java/how-to/scaling', + label: 'Scale Java runtime service', + }, + ], }, { type: 'category', - label: 'How-to', + label: 'Build & Deployment', collapsible: false, customProps: { sidebar_is_group_headline: true, @@ -2559,42 +1851,53 @@ module.exports = { items: [ { type: 'doc', - id: 'keydb/how-to/create', - label: 'Create KeyDB service', + id: 'java/how-to/build-pipeline', + label: 'Configure build & deploy pipeline', }, { type: 'doc', - id: 'keydb/how-to/connect', - label: 'Connect to KeyDB', + id: 'java/how-to/trigger-pipeline', + label: 'Trigger build pipeline', }, { type: 'doc', - id: 'keydb/how-to/manage', - label: 'Manage users and databases', + id: 'java/how-to/build-process', + label: 'Build process', }, { type: 'doc', - id: 'keydb/how-to/scale', - label: 'Scale KeyDB service', + id: 'java/how-to/deploy-process', + label: 'Deploy process', + }, + ], + }, + { + type: 'category', + label: 'Maintenance & Monitoring', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'java/how-to/logs', + label: 'Setup & access logs', }, { type: 'doc', - id: 'keydb/how-to/control', - label: 'Stop, start and delete KeyDB service', + id: 'java/how-to/filebrowser', + label: 'Browse container files', + }, + { + type: 'doc', + id: 'java/how-to/shared-storage', + label: 'Connect / disconnect shared storage', }, ], }, - // { - // type: 'doc', - // id: 'keydb/faq', - // label: 'FAQ', - // customProps: { - // sidebar_is_title: true, - // sidebar_icon: 'chat-bubble-left-right', - // }, - // }, ], - typesense: [ + nginx: [ { type: 'ref', id: 'homepage', @@ -2606,35 +1909,129 @@ module.exports = { }, { type: 'doc', - id: 'typesense/overview', - label: 'Zerops Typesense Service', + id: 'nginx/overview', + label: 'Nginx Static', customProps: { sidebar_is_title: true, - sidebar_icon: 'typesense', + sidebar_icon: 'nginx', }, }, - ], - meilisearch: [ { - type: 'ref', - id: 'homepage', - label: 'Back to home', + type: 'category', + label: 'Management', + collapsible: false, customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'nginx/how-to/create', + label: 'Create Nginx static service', + }, + { + type: 'doc', + id: 'nginx/how-to/upgrade', + label: 'Upgrade Nginx service', + }, + { + type: 'doc', + id: 'nginx/how-to/controls', + label: 'Stop, start & delete Nginx static service', + }, + ], + }, + { + type: 'category', + label: 'Configuration & Environment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'nginx/how-to/env-variables', + label: 'Manage environment variables', + }, + { + type: 'doc', + id: 'nginx/how-to/customize-runtime', + label: 'Customize Nginx static runtime', + }, + { + type: 'doc', + id: 'nginx/how-to/customize-web-server', + label: 'Customize web server', + }, + { + type: 'doc', + id: 'nginx/how-to/scaling', + label: 'Scale Nginx static service', + }, + ], + }, + { + type: 'category', + label: 'Build & Deployment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'nginx/how-to/build-pipeline', + label: 'Configure build & deploy pipeline', + }, + { + type: 'doc', + id: 'nginx/how-to/trigger-pipeline', + label: 'Trigger build pipeline', + }, + { + type: 'doc', + id: 'nginx/how-to/deploy-process', + label: 'Deploy process', + }, + ], + }, + { + type: 'category', + label: 'Maintenance & Monitoring', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, }, + items: [ + { + type: 'doc', + id: 'nginx/how-to/logs', + label: 'Setup & access logs', + }, + { + type: 'doc', + id: 'nginx/how-to/filebrowser', + label: 'Browse container files', + }, + { + type: 'doc', + id: 'nginx/how-to/shared-storage', + label: 'Connect / disconnect shared storage', + }, + ], }, { type: 'doc', - id: 'meilisearch/overview', - label: 'Zerops Meilisearch Service', + id: 'nginx/faq', + label: 'FAQ', customProps: { sidebar_is_title: true, - sidebar_icon: 'meilisearch', + sidebar_icon: 'chat-bubble-left-right', }, }, ], - valkey: [ + static: [ { type: 'ref', id: 'homepage', @@ -2646,15 +2043,15 @@ module.exports = { }, { type: 'doc', - id: 'valkey/overview', - label: 'Zerops Valkey Service', + id: 'static/overview', + label: 'Static Service', customProps: { sidebar_is_title: true, - sidebar_icon: 'valkey', + sidebar_icon: 'computer-desktop', }, }, ], - qdrant: [ + ubuntu: [ { type: 'ref', id: 'homepage', @@ -2666,75 +2063,120 @@ module.exports = { }, { type: 'doc', - id: 'qdrant/overview', - label: 'Zerops Qdrant Service', + id: 'ubuntu/overview', + label: 'Ubuntu', customProps: { sidebar_is_title: true, - sidebar_icon: 'qdrant', + sidebar_icon: 'ubuntu', }, }, - ], - nats: [ { - type: 'ref', - id: 'homepage', - label: 'Back to home', + type: 'category', + label: 'Management', + collapsible: false, customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', + sidebar_is_group_headline: true, }, + items: [ + { + type: 'doc', + id: 'ubuntu/how-to/create', + label: 'Create Ubuntu service', + }, + { + type: 'doc', + id: 'ubuntu/how-to/upgrade', + label: 'Upgrade Ubuntu service', + }, + { + type: 'doc', + id: 'ubuntu/how-to/controls', + label: 'Stop, start & delete Ubuntu runtime service', + }, + ], }, { - type: 'doc', - id: 'nats/overview', - label: 'Zerops NATS Service', + type: 'category', + label: 'Configuration & Environment', + collapsible: false, customProps: { - sidebar_is_title: true, - sidebar_icon: 'nats', + sidebar_is_group_headline: true, }, + items: [ + { + type: 'doc', + id: 'ubuntu/how-to/env-variables', + label: 'Manage environment variables', + }, + { + type: 'doc', + id: 'ubuntu/how-to/customize-runtime', + label: 'Customize Ubuntu runtime', + }, + { + type: 'doc', + id: 'ubuntu/how-to/scaling', + label: 'Scale Ubuntu runtime service', + }, + ], }, - ], - kafka: [ { - type: 'ref', - id: 'homepage', - label: 'Back to home', + type: 'category', + label: 'Build & Deployment', + collapsible: false, customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', + sidebar_is_group_headline: true, }, + items: [ + { + type: 'doc', + id: 'ubuntu/how-to/build-pipeline', + label: 'Configure build & deploy pipeline', + }, + { + type: 'doc', + id: 'ubuntu/how-to/trigger-pipeline', + label: 'Trigger build pipeline', + }, + { + type: 'doc', + id: 'ubuntu/how-to/build-process', + label: 'Build process', + }, + { + type: 'doc', + id: 'ubuntu/how-to/deploy-process', + label: 'Deploy process', + }, + ], }, { - type: 'doc', - id: 'kafka/overview', - label: 'Zerops Kafka Service', + type: 'category', + label: 'Maintenance & Monitoring', + collapsible: false, customProps: { - sidebar_is_title: true, - sidebar_icon: 'kafka', + sidebar_is_group_headline: true, }, - }, - ], - clickhouse: [ - { - type: 'ref', - id: 'homepage', - label: 'Back to home', - customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', + items: [ + { + type: 'doc', + id: 'ubuntu/how-to/logs', + label: 'Setup & access logs', }, - }, - { - type: 'doc', - id: 'clickhouse/overview', - label: 'Zerops Clickhouse Service', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'clickhouse', + { + type: 'doc', + id: 'ubuntu/how-to/filebrowser', + label: 'Browse container files', }, - }, - ], - sharedstorage: [ + { + type: 'doc', + id: 'ubuntu/how-to/shared-storage', + label: 'Connect / disconnect shared storage', + }, + ], + }, + ], + alpine: [ { type: 'ref', id: 'homepage', @@ -2746,60 +2188,140 @@ module.exports = { }, { type: 'doc', - id: 'shared-storage/overview', - label: 'Shared storage overview', + id: 'alpine/overview', + label: 'Alpine', customProps: { sidebar_is_title: true, - sidebar_icon: 'server', + sidebar_icon: 'alpine', }, }, { type: 'category', - label: 'How-to', + label: 'Management', collapsible: false, customProps: { sidebar_is_group_headline: true, - sidebar_icon: 'academic-cap-solid', }, items: [ { type: 'doc', - id: 'shared-storage/how-to/create', - label: 'Create shared storage', + id: 'alpine/how-to/create', + label: 'Create Alpine service', }, { type: 'doc', - id: 'shared-storage/how-to/connect', - label: 'Connect shared storage', + id: 'alpine/how-to/upgrade', + label: 'Upgrade Alpine service', }, { type: 'doc', - id: 'shared-storage/how-to/use', - label: 'Usage & Limitations', + id: 'alpine/how-to/controls', + label: 'Stop, start & delete Alpine runtime service', + }, + ], + }, + { + type: 'category', + label: 'Configuration & Environment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'alpine/how-to/env-variables', + label: 'Manage environment variables', }, { type: 'doc', - id: 'shared-storage/how-to/manage', - label: 'Manage & Access shared storage', + id: 'alpine/how-to/customize-runtime', + label: 'Customize Alpine runtime', }, { type: 'doc', - id: 'shared-storage/how-to/backup', - label: 'Backup shared storage', + id: 'alpine/how-to/scaling', + label: 'Scale Alpine runtime service', + }, + ], + }, + { + type: 'category', + label: 'Build & Deployment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'alpine/how-to/build-pipeline', + label: 'Configure build & deploy pipeline', + }, + { + type: 'doc', + id: 'alpine/how-to/trigger-pipeline', + label: 'Trigger build pipeline', + }, + { + type: 'doc', + id: 'alpine/how-to/build-process', + label: 'Build process', + }, + { + type: 'doc', + id: 'alpine/how-to/deploy-process', + label: 'Deploy process', + }, + ], + }, + { + type: 'category', + label: 'Maintenance & Monitoring', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'alpine/how-to/logs', + label: 'Setup & access logs', + }, + { + type: 'doc', + id: 'alpine/how-to/filebrowser', + label: 'Browse container files', + }, + { + type: 'doc', + id: 'alpine/how-to/shared-storage', + label: 'Connect / disconnect shared storage', }, ], }, + ], + docker: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, { type: 'doc', - id: 'shared-storage/tech-details', - label: 'Technical details', + id: 'docker/overview', + label: 'Docker Service', customProps: { sidebar_is_title: true, - sidebar_icon: 'document-text', + sidebar_icon: 'docker', }, }, ], - objectstorage: [ + mariadb: [ { type: 'ref', id: 'homepage', @@ -2811,11 +2333,11 @@ module.exports = { }, { type: 'doc', - id: 'object-storage/overview', - label: 'Object Storage Overview', + id: 'mariadb/overview', + label: 'Zerops MariaDB Service', customProps: { sidebar_is_title: true, - sidebar_icon: 'server', + sidebar_icon: 'mariadb', }, }, { @@ -2824,725 +2346,1202 @@ module.exports = { collapsible: false, customProps: { sidebar_is_group_headline: true, - sidebar_icon: 'academic-cap-solid', }, items: [ { type: 'doc', - id: 'object-storage/how-to/create', - label: 'Create object storage service', + id: 'mariadb/how-to/create', + label: 'Create MariaDB service', }, { type: 'doc', - id: 'object-storage/how-to/update-bucket', - label: 'Change bucket size or access policy', + id: 'mariadb/how-to/connect', + label: 'Connect to MariaDB', }, { type: 'doc', - id: 'object-storage/how-to/access', - label: 'Access object storage service', + id: 'mariadb/how-to/manage', + label: 'Manage users and databases', }, { type: 'doc', - id: 'object-storage/how-to/controls', - label: 'Stop, start & delete object storage service', + id: 'mariadb/how-to/export-import-data', + label: 'Export and import data', }, { type: 'doc', - id: 'object-storage/how-to/curl-file', - label: 'Download file from a private bucket', + id: 'mariadb/how-to/backup', + label: 'Backup & restore data', + }, + { + type: 'doc', + id: 'mariadb/how-to/scale', + label: 'Scale MariaDB service', + }, + { + type: 'doc', + id: 'mariadb/how-to/control', + label: 'Stop, start and delete MariaDB service', }, ], }, - ], - deno: [ - { - type: 'ref', - id: 'homepage', - label: 'Back to home', - customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', - }, + { + type: 'category', + label: 'Technical details', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, }, - { - type: 'doc', - id: 'deno/overview', - label: 'Deno', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'deno', - }, - }, - { - type: 'category', - label: 'Management', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'deno/how-to/create', - label: 'Create Deno service', - }, - { - type: 'doc', - id: 'deno/how-to/upgrade', - label: 'Upgrade Deno service', - }, - { - type: 'doc', - id: 'deno/how-to/controls', - label: 'Stop, start & delete Deno runtime service', - }, - ], + items: [ + { + type: 'doc', + id: 'mariadb/tech-details/cluster', + label: 'MariaDB cluster asynchronous behaviour', + }, + { + type: 'doc', + id: 'mariadb/tech-details/limitations', + label: 'Technical limitations of MariaDB cluster', + }, + ], + }, + // { + // type: 'doc', + // id: 'mariadb/faq', + // label: 'FAQ', + // customProps: { + // sidebar_is_title: true, + // sidebar_icon: 'chat-bubble-left-right', + // }, + // }, + ], + postgresql: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'postgresql/overview', + label: 'Zerops PostgreSQL Service', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'postgresql', + }, + }, + { + type: 'category', + label: 'How-to', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'postgresql/how-to/create', + label: 'Create PostgreSQL service', + }, + { + type: 'doc', + id: 'postgresql/how-to/connect', + label: 'Connect to PostgreSQL', + }, + { + type: 'doc', + id: 'postgresql/how-to/manage', + label: 'Manage users, databases & plugins', + }, + { + type: 'doc', + id: 'postgresql/how-to/export-import-data', + label: 'Export and import data', + }, + { + type: 'doc', + id: 'postgresql/how-to/backup', + label: 'Backup data', + }, + { + type: 'doc', + id: 'postgresql/how-to/scale', + label: 'Scale PostgreSQL service', + }, + { + type: 'doc', + id: 'postgresql/how-to/control', + label: 'Stop, start and delete PostgreSQL service', + }, + ], + }, + // { + // type: "category", + // label: "Technical details", + // collapsible: false, + // customProps: { + // sidebar_is_group_headline: true, + // }, + // items: [ + // { + // type: "doc", + // id: "postgresql/tech-details/cluster", + // label: "PostgreSQL cluster asynchronous behaviour", + // }, + // { + // type: "doc", + // id: "postgresql/tech-details/limitations", + // label: "Technical limitations of PostgreSQL cluster", + // }, + // ], + // }, + { + type: 'doc', + id: 'postgresql/faq', + label: 'FAQ', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'chat-bubble-left-right', + }, + }, + ], + elasticsearch: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'elasticsearch/overview', + label: 'Zerops Elasticsearch Service', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'elasticsearch', + }, + }, + ], + keydb: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'keydb/overview', + label: 'Zerops KeyDB Service', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'keydb', + }, + }, + { + type: 'category', + label: 'How-to', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'keydb/how-to/create', + label: 'Create KeyDB service', + }, + { + type: 'doc', + id: 'keydb/how-to/connect', + label: 'Connect to KeyDB', + }, + { + type: 'doc', + id: 'keydb/how-to/manage', + label: 'Manage users and databases', + }, + { + type: 'doc', + id: 'keydb/how-to/scale', + label: 'Scale KeyDB service', + }, + { + type: 'doc', + id: 'keydb/how-to/control', + label: 'Stop, start and delete KeyDB service', + }, + ], + }, + // { + // type: 'doc', + // id: 'keydb/faq', + // label: 'FAQ', + // customProps: { + // sidebar_is_title: true, + // sidebar_icon: 'chat-bubble-left-right', + // }, + // }, + ], + typesense: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'typesense/overview', + label: 'Zerops Typesense Service', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'typesense', + }, + }, + ], + meilisearch: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'meilisearch/overview', + label: 'Zerops Meilisearch Service', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'meilisearch', + }, + }, + ], + valkey: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'valkey/overview', + label: 'Zerops Valkey Service', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'valkey', + }, + }, + ], + qdrant: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'qdrant/overview', + label: 'Zerops Qdrant Service', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'qdrant', + }, + }, + ], + nats: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'nats/overview', + label: 'Zerops NATS Service', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'nats', + }, + }, + ], + kafka: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'kafka/overview', + label: 'Zerops Kafka Service', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'kafka', + }, + }, + ], + clickhouse: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'clickhouse/overview', + label: 'Zerops Clickhouse Service', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'clickhouse', + }, + }, + ], + sharedstorage: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'shared-storage/overview', + label: 'Shared storage overview', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'server', + }, + }, + { + type: 'category', + label: 'How-to', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + sidebar_icon: 'academic-cap-solid', + }, + items: [ + { + type: 'doc', + id: 'shared-storage/how-to/create', + label: 'Create shared storage', + }, + { + type: 'doc', + id: 'shared-storage/how-to/connect', + label: 'Connect shared storage', + }, + { + type: 'doc', + id: 'shared-storage/how-to/use', + label: 'Usage & Limitations', + }, + { + type: 'doc', + id: 'shared-storage/how-to/manage', + label: 'Manage & Access shared storage', + }, + { + type: 'doc', + id: 'shared-storage/how-to/backup', + label: 'Backup shared storage', + }, + ], + }, + { + type: 'doc', + id: 'shared-storage/tech-details', + label: 'Technical details', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'document-text', + }, + }, + ], + objectstorage: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'object-storage/overview', + label: 'Object Storage Overview', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'server', + }, + }, + { + type: 'category', + label: 'How-to', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + sidebar_icon: 'academic-cap-solid', + }, + items: [ + { + type: 'doc', + id: 'object-storage/how-to/create', + label: 'Create object storage service', + }, + { + type: 'doc', + id: 'object-storage/how-to/update-bucket', + label: 'Change bucket size or access policy', + }, + { + type: 'doc', + id: 'object-storage/how-to/access', + label: 'Access object storage service', + }, + { + type: 'doc', + id: 'object-storage/how-to/controls', + label: 'Stop, start & delete object storage service', + }, + { + type: 'doc', + id: 'object-storage/how-to/curl-file', + label: 'Download file from a private bucket', + }, + ], + }, + ], + deno: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'deno/overview', + label: 'Deno', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'deno', + }, + }, + { + type: 'category', + label: 'Management', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'deno/how-to/create', + label: 'Create Deno service', + }, + { + type: 'doc', + id: 'deno/how-to/upgrade', + label: 'Upgrade Deno service', + }, + { + type: 'doc', + id: 'deno/how-to/controls', + label: 'Stop, start & delete Deno runtime service', + }, + ], + }, + { + type: 'category', + label: 'Configuration & Environment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, }, - { - type: 'category', - label: 'Configuration & Environment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, + items: [ + { + type: 'doc', + id: 'deno/how-to/env-variables', + label: 'Manage environment variables', }, - items: [ - { - type: 'doc', - id: 'deno/how-to/env-variables', - label: 'Manage environment variables', - }, - { - type: 'doc', - id: 'deno/how-to/customize-runtime', - label: 'Customize Deno runtime', - }, - { - type: 'doc', - id: 'deno/how-to/scaling', - label: 'Scale Deno runtime service', - }, - ], + { + type: 'doc', + id: 'deno/how-to/customize-runtime', + label: 'Customize Deno runtime', + }, + { + type: 'doc', + id: 'deno/how-to/scaling', + label: 'Scale Deno runtime service', + }, + ], + }, + { + type: 'category', + label: 'Build & Deployment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'deno/how-to/build-pipeline', + label: 'Configure build & deploy pipeline', + }, + { + type: 'doc', + id: 'deno/how-to/trigger-pipeline', + label: 'Trigger build pipeline', + }, + { + type: 'doc', + id: 'deno/how-to/build-process', + label: 'Build process', + }, + { + type: 'doc', + id: 'deno/how-to/deploy-process', + label: 'Deploy process', + }, + ], + }, + { + type: 'category', + label: 'Maintenance & Monitoring', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'deno/how-to/logs', + label: 'Setup & access logs', + }, + { + type: 'doc', + id: 'deno/how-to/filebrowser', + label: 'Browse container files', + }, + { + type: 'doc', + id: 'deno/how-to/shared-storage', + label: 'Connect / disconnect shared storage', + }, + ], + }, + ], + bun: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'bun/overview', + label: 'Bun', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'bun', + }, + }, + { + type: 'category', + label: 'Management', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'bun/how-to/create', + label: 'Create Bun service', + }, + { + type: 'doc', + id: 'bun/how-to/upgrade', + label: 'Upgrade Bun service', + }, + { + type: 'doc', + id: 'bun/how-to/controls', + label: 'Stop, start & delete Bun runtime service', + }, + ], + }, + { + type: 'category', + label: 'Configuration & Environment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'bun/how-to/env-variables', + label: 'Manage environment variables', + }, + { + type: 'doc', + id: 'bun/how-to/customize-runtime', + label: 'Customize Bun runtime', + }, + { + type: 'doc', + id: 'bun/how-to/scaling', + label: 'Scale Bun runtime service', + }, + ], + }, + { + type: 'category', + label: 'Build & Deployment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'bun/how-to/build-pipeline', + label: 'Configure build & deploy pipeline', + }, + { + type: 'doc', + id: 'bun/how-to/trigger-pipeline', + label: 'Trigger build pipeline', + }, + { + type: 'doc', + id: 'bun/how-to/build-process', + label: 'Build process', + }, + { + type: 'doc', + id: 'bun/how-to/deploy-process', + label: 'Deploy process', + }, + ], + }, + { + type: 'category', + label: 'Maintenance & Monitoring', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'bun/how-to/logs', + label: 'Setup & access logs', + }, + { + type: 'doc', + id: 'bun/how-to/filebrowser', + label: 'Browse container files', + }, + { + type: 'doc', + id: 'bun/how-to/shared-storage', + label: 'Connect / disconnect shared storage', + }, + ], + }, + ], + gleam: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', + }, + }, + { + type: 'doc', + id: 'gleam/overview', + label: 'Gleam', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'gleam', + }, + }, + { + type: 'category', + label: 'Management', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, + }, + items: [ + { + type: 'doc', + id: 'gleam/how-to/create', + label: 'Create Gleam service', + }, + { + type: 'doc', + id: 'gleam/how-to/upgrade', + label: 'Upgrade Gleam service', + }, + { + type: 'doc', + id: 'gleam/how-to/controls', + label: 'Stop, start & delete Gleam runtime service', + }, + ], + }, + { + type: 'category', + label: 'Configuration & Environment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, }, - { - type: 'category', - label: 'Build & Deployment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, + items: [ + { + type: 'doc', + id: 'gleam/how-to/env-variables', + label: 'Manage environment variables', }, - items: [ - { - type: 'doc', - id: 'deno/how-to/build-pipeline', - label: 'Configure build & deploy pipeline', - }, - { - type: 'doc', - id: 'deno/how-to/trigger-pipeline', - label: 'Trigger build pipeline', - }, - { - type: 'doc', - id: 'deno/how-to/build-process', - label: 'Build process', - }, - { - type: 'doc', - id: 'deno/how-to/deploy-process', - label: 'Deploy process', - }, - ], - }, - { - type: 'category', - label: 'Maintenance & Monitoring', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, + { + type: 'doc', + id: 'gleam/how-to/customize-runtime', + label: 'Customize Gleam runtime', }, - items: [ - { - type: 'doc', - id: 'deno/how-to/logs', - label: 'Setup & access logs', - }, - { - type: 'doc', - id: 'deno/how-to/filebrowser', - label: 'Browse container files', - }, - { - type: 'doc', - id: 'deno/how-to/shared-storage', - label: 'Connect / disconnect shared storage', - }, - ], - }, - ], - bun: [ - { - type: 'ref', - id: 'homepage', - label: 'Back to home', - customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', + { + type: 'doc', + id: 'gleam/how-to/scaling', + label: 'Scale Gleam runtime service', }, + ], + }, + { + type: 'category', + label: 'Build & Deployment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, }, - { - type: 'doc', - id: 'bun/overview', - label: 'Bun', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'bun', - }, - }, - { - type: 'category', - label: 'Management', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'bun/how-to/create', - label: 'Create Bun service', - }, - { - type: 'doc', - id: 'bun/how-to/upgrade', - label: 'Upgrade Bun service', - }, - { - type: 'doc', - id: 'bun/how-to/controls', - label: 'Stop, start & delete Bun runtime service', - }, - ], - }, - { - type: 'category', - label: 'Configuration & Environment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, + items: [ + { + type: 'doc', + id: 'gleam/how-to/build-pipeline', + label: 'Configure build & deploy pipeline', }, - items: [ - { - type: 'doc', - id: 'bun/how-to/env-variables', - label: 'Manage environment variables', - }, - { - type: 'doc', - id: 'bun/how-to/customize-runtime', - label: 'Customize Bun runtime', - }, - { - type: 'doc', - id: 'bun/how-to/scaling', - label: 'Scale Bun runtime service', - }, - ], - }, - { - type: 'category', - label: 'Build & Deployment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, + { + type: 'doc', + id: 'gleam/how-to/trigger-pipeline', + label: 'Trigger build pipeline', }, - items: [ - { - type: 'doc', - id: 'bun/how-to/build-pipeline', - label: 'Configure build & deploy pipeline', - }, - { - type: 'doc', - id: 'bun/how-to/trigger-pipeline', - label: 'Trigger build pipeline', - }, - { - type: 'doc', - id: 'bun/how-to/build-process', - label: 'Build process', - }, - { - type: 'doc', - id: 'bun/how-to/deploy-process', - label: 'Deploy process', - }, - ], - }, - { - type: 'category', - label: 'Maintenance & Monitoring', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, + { + type: 'doc', + id: 'gleam/how-to/build-process', + label: 'Build process', }, - items: [ - { - type: 'doc', - id: 'bun/how-to/logs', - label: 'Setup & access logs', - }, - { - type: 'doc', - id: 'bun/how-to/filebrowser', - label: 'Browse container files', - }, - { - type: 'doc', - id: 'bun/how-to/shared-storage', - label: 'Connect / disconnect shared storage', - }, - ], + { + type: 'doc', + id: 'gleam/how-to/deploy-process', + label: 'Deploy process', + }, + ], + }, + { + type: 'category', + label: 'Maintenance & Monitoring', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, }, - ], - gleam: [ - { - type: 'ref', - id: 'homepage', - label: 'Back to home', - customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', + items: [ + { + type: 'doc', + id: 'gleam/how-to/logs', + label: 'Setup & access logs', + }, + { + type: 'doc', + id: 'gleam/how-to/filebrowser', + label: 'Browse container files', + }, + { + type: 'doc', + id: 'gleam/how-to/shared-storage', + label: 'Connect / disconnect shared storage', }, + ], + }, + ], + elixir: [ + { + type: 'ref', + id: 'homepage', + label: 'Back to home', + customProps: { + sidebar_is_back_link: true, + sidebar_icon: 'back-arrow', }, - { - type: 'doc', - id: 'gleam/overview', - label: 'Gleam', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'gleam', - }, - }, - { - type: 'category', - label: 'Management', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'gleam/how-to/create', - label: 'Create Gleam service', - }, - { - type: 'doc', - id: 'gleam/how-to/upgrade', - label: 'Upgrade Gleam service', - }, - { - type: 'doc', - id: 'gleam/how-to/controls', - label: 'Stop, start & delete Gleam runtime service', - }, - ], + }, + { + type: 'doc', + id: 'elixir/overview', + label: 'Elixir', + customProps: { + sidebar_is_title: true, + sidebar_icon: 'elixir', }, - { - type: 'category', - label: 'Configuration & Environment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'gleam/how-to/env-variables', - label: 'Manage environment variables', - }, - { - type: 'doc', - id: 'gleam/how-to/customize-runtime', - label: 'Customize Gleam runtime', - }, - { - type: 'doc', - id: 'gleam/how-to/scaling', - label: 'Scale Gleam runtime service', - }, - ], + }, + { + type: 'category', + label: 'Management', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, }, - { - type: 'category', - label: 'Build & Deployment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, + items: [ + { + type: 'doc', + id: 'elixir/how-to/create', + label: 'Create Elixir service', }, - items: [ - { - type: 'doc', - id: 'gleam/how-to/build-pipeline', - label: 'Configure build & deploy pipeline', - }, - { - type: 'doc', - id: 'gleam/how-to/trigger-pipeline', - label: 'Trigger build pipeline', - }, - { - type: 'doc', - id: 'gleam/how-to/build-process', - label: 'Build process', - }, - { - type: 'doc', - id: 'gleam/how-to/deploy-process', - label: 'Deploy process', - }, - ], - }, - { - type: 'category', - label: 'Maintenance & Monitoring', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, + { + type: 'doc', + id: 'elixir/how-to/upgrade', + label: 'Upgrade Elixir service', }, - items: [ - { - type: 'doc', - id: 'gleam/how-to/logs', - label: 'Setup & access logs', - }, - { - type: 'doc', - id: 'gleam/how-to/filebrowser', - label: 'Browse container files', - }, - { - type: 'doc', - id: 'gleam/how-to/shared-storage', - label: 'Connect / disconnect shared storage', - }, - ], - }, - ], - elixir: [ - { - type: 'ref', - id: 'homepage', - label: 'Back to home', - customProps: { - sidebar_is_back_link: true, - sidebar_icon: 'back-arrow', + { + type: 'doc', + id: 'elixir/how-to/controls', + label: 'Stop, start & delete Elixir runtime service', }, + ], + }, + { + type: 'category', + label: 'Configuration & Environment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, }, - { - type: 'doc', - id: 'elixir/overview', - label: 'Elixir', - customProps: { - sidebar_is_title: true, - sidebar_icon: 'elixir', - }, - }, - { - type: 'category', - label: 'Management', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, - }, - items: [ - { - type: 'doc', - id: 'elixir/how-to/create', - label: 'Create Elixir service', - }, - { - type: 'doc', - id: 'elixir/how-to/upgrade', - label: 'Upgrade Elixir service', - }, - { - type: 'doc', - id: 'elixir/how-to/controls', - label: 'Stop, start & delete Elixir runtime service', - }, - ], - }, - { - type: 'category', - label: 'Configuration & Environment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, + items: [ + { + type: 'doc', + id: 'elixir/how-to/env-variables', + label: 'Manage environment variables', }, - items: [ - { - type: 'doc', - id: 'elixir/how-to/env-variables', - label: 'Manage environment variables', - }, - { - type: 'doc', - id: 'elixir/how-to/customize-runtime', - label: 'Customize Elixir runtime', - }, - { - type: 'doc', - id: 'elixir/how-to/scaling', - label: 'Scale Elixir runtime service', - }, - ], + { + type: 'doc', + id: 'elixir/how-to/customize-runtime', + label: 'Customize Elixir runtime', + }, + { + type: 'doc', + id: 'elixir/how-to/scaling', + label: 'Scale Elixir runtime service', + }, + ], + }, + { + type: 'category', + label: 'Build & Deployment', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, }, - { - type: 'category', - label: 'Build & Deployment', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, + items: [ + { + type: 'doc', + id: 'elixir/how-to/build-pipeline', + label: 'Configure build & deploy pipeline', }, - items: [ - { - type: 'doc', - id: 'elixir/how-to/build-pipeline', - label: 'Configure build & deploy pipeline', - }, - { - type: 'doc', - id: 'elixir/how-to/trigger-pipeline', - label: 'Trigger build pipeline', - }, - { - type: 'doc', - id: 'elixir/how-to/build-process', - label: 'Build process', - }, - { - type: 'doc', - id: 'elixir/how-to/deploy-process', - label: 'Deploy process', - }, - ], + { + type: 'doc', + id: 'elixir/how-to/trigger-pipeline', + label: 'Trigger build pipeline', + }, + { + type: 'doc', + id: 'elixir/how-to/build-process', + label: 'Build process', + }, + { + type: 'doc', + id: 'elixir/how-to/deploy-process', + label: 'Deploy process', + }, + ], + }, + { + type: 'category', + label: 'Maintenance & Monitoring', + collapsible: false, + customProps: { + sidebar_is_group_headline: true, }, - { - type: 'category', - label: 'Maintenance & Monitoring', - collapsible: false, - customProps: { - sidebar_is_group_headline: true, + items: [ + { + type: 'doc', + id: 'elixir/how-to/logs', + label: 'Setup & access logs', }, - items: [ - { - type: 'doc', - id: 'elixir/how-to/logs', - label: 'Setup & access logs', - }, - { - type: 'doc', - id: 'elixir/how-to/filebrowser', - label: 'Browse container files', - }, - { - type: 'doc', - id: 'elixir/how-to/shared-storage', - label: 'Connect / disconnect shared storage', - }, - ], - }, - ], -// laravel: [ -// { -// type: 'ref', -// id: 'homepage', -// label: 'Back to home', -// customProps: { -// sidebar_is_back_link: true, -// sidebar_icon: 'back-arrow', -// }, -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel', -// label: 'Laravel', -// customProps: { -// sidebar_is_title: true, -// sidebar_icon: 'laravel', -// }, -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/introduction', -// label: 'Quickstart Guide', -// customProps: { -// sidebar_icon: 'rocket-launch', -// }, -// }, -// { -// type: 'category', -// label: 'Features', -// collapsible: false, -// customProps: { -// sidebar_is_group_headline: true, -// }, -// items: [ -// { -// type: 'doc', -// id: 'frameworks/laravel/env-variables', -// label: 'Environment Variables', -// }, -//// { -//// type: 'doc', -//// id: 'frameworks/laravel/local-development', -//// label: 'Local Development', -//// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/migrations', -// label: 'Database Migrations', -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/redis', -// label: 'Cache & Queue with Redis', -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/cron', -// label: 'Schedule Jobs & CRON', -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/smtp', -// label: 'SMTP Configuration', -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/logs', -// label: 'Logs', -// }, -// ], -// }, -// { -// type: 'html', -// value: 'Recipes', -// customProps: { -// sidebar_is_group_divider: true, -// }, -// className: 'homepage-sidebar-item', -// }, -// { -// type: 'category', -// label: 'Minimal', -// collapsible: true, -// customProps: { -// sidebar_is_group_headline: true, -// }, -// items: [ -// { -// type: 'doc', -// id: 'frameworks/laravel/recipes/minimal-local', -// label: 'Local Development', -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/recipes/minimal-devel', -// label: 'Stage Environment', -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/recipes/minimal-prod', -// label: 'Production', -// }, -// ], -// }, -// { -// type: 'category', -// label: 'Jetstream', -// collapsible: true, -// customProps: { -// sidebar_is_group_headline: true, -// }, -// items: [ -// { -// type: 'doc', -// id: 'frameworks/laravel/recipes/jetstream-local', -// label: 'Local Development', -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/recipes/jetstream-devel', -// label: 'Stage Environment', -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/recipes/jetstream-prod', -// label: 'Production', -// }, -// ], -// }, -// { -// type: 'category', -// label: 'Filament', -// collapsible: true, -// customProps: { -// sidebar_is_group_headline: true, -// }, -// items: [ -// { -// type: 'doc', -// id: 'frameworks/laravel/recipes/filament-local', -// label: 'Local Development', -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/recipes/filament-devel', -// label: 'Stage Environment', -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/recipes/filament-prod', -// label: 'Production', -// }, -// ], -// }, -// { -// type: 'category', -// label: 'Twill CMS', -// collapsible: true, -// customProps: { -// sidebar_is_group_headline: true, -// }, -// items: [ -// { -// type: 'doc', -// id: 'frameworks/laravel/recipes/twill-local', -// label: 'Local Development', -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/recipes/twill-devel', -// label: 'Stage Environment', -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/recipes/twill-prod', -// label: 'Production', -// }, -// ], -// }, -// { -// type: 'doc', -// id: 'frameworks/laravel/faq', -// label: 'FAQ', -// customProps: { -// sidebar_is_title: true, -// sidebar_icon: 'chat-bubble-left-right', -// }, -// }, -// ], + { + type: 'doc', + id: 'elixir/how-to/filebrowser', + label: 'Browse container files', + }, + { + type: 'doc', + id: 'elixir/how-to/shared-storage', + label: 'Connect / disconnect shared storage', + }, + ], + }, + ], + // laravel: [ + // { + // type: 'ref', + // id: 'homepage', + // label: 'Back to home', + // customProps: { + // sidebar_is_back_link: true, + // sidebar_icon: 'back-arrow', + // }, + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel', + // label: 'Laravel', + // customProps: { + // sidebar_is_title: true, + // sidebar_icon: 'laravel', + // }, + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/introduction', + // label: 'Quickstart Guide', + // customProps: { + // sidebar_icon: 'rocket-launch', + // }, + // }, + // { + // type: 'category', + // label: 'Features', + // collapsible: false, + // customProps: { + // sidebar_is_group_headline: true, + // }, + // items: [ + // { + // type: 'doc', + // id: 'frameworks/laravel/env-variables', + // label: 'Environment Variables', + // }, + //// { + //// type: 'doc', + //// id: 'frameworks/laravel/local-development', + //// label: 'Local Development', + //// }, + // { + // type: 'doc', + // id: 'frameworks/laravel/migrations', + // label: 'Database Migrations', + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/redis', + // label: 'Cache & Queue with Redis', + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/cron', + // label: 'Schedule Jobs & CRON', + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/smtp', + // label: 'SMTP Configuration', + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/logs', + // label: 'Logs', + // }, + // ], + // }, + // { + // type: 'html', + // value: 'Recipes', + // customProps: { + // sidebar_is_group_divider: true, + // }, + // className: 'homepage-sidebar-item', + // }, + // { + // type: 'category', + // label: 'Minimal', + // collapsible: true, + // customProps: { + // sidebar_is_group_headline: true, + // }, + // items: [ + // { + // type: 'doc', + // id: 'frameworks/laravel/recipes/minimal-local', + // label: 'Local Development', + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/recipes/minimal-devel', + // label: 'Stage Environment', + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/recipes/minimal-prod', + // label: 'Production', + // }, + // ], + // }, + // { + // type: 'category', + // label: 'Jetstream', + // collapsible: true, + // customProps: { + // sidebar_is_group_headline: true, + // }, + // items: [ + // { + // type: 'doc', + // id: 'frameworks/laravel/recipes/jetstream-local', + // label: 'Local Development', + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/recipes/jetstream-devel', + // label: 'Stage Environment', + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/recipes/jetstream-prod', + // label: 'Production', + // }, + // ], + // }, + // { + // type: 'category', + // label: 'Filament', + // collapsible: true, + // customProps: { + // sidebar_is_group_headline: true, + // }, + // items: [ + // { + // type: 'doc', + // id: 'frameworks/laravel/recipes/filament-local', + // label: 'Local Development', + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/recipes/filament-devel', + // label: 'Stage Environment', + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/recipes/filament-prod', + // label: 'Production', + // }, + // ], + // }, + // { + // type: 'category', + // label: 'Twill CMS', + // collapsible: true, + // customProps: { + // sidebar_is_group_headline: true, + // }, + // items: [ + // { + // type: 'doc', + // id: 'frameworks/laravel/recipes/twill-local', + // label: 'Local Development', + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/recipes/twill-devel', + // label: 'Stage Environment', + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/recipes/twill-prod', + // label: 'Production', + // }, + // ], + // }, + // { + // type: 'doc', + // id: 'frameworks/laravel/faq', + // label: 'FAQ', + // customProps: { + // sidebar_is_title: true, + // sidebar_icon: 'chat-bubble-left-right', + // }, + // }, + // ], }; From 3ab5317d018c1eda39e18c3da7e372ddc7d27e46 Mon Sep 17 00:00:00 2001 From: krls2020 Date: Sun, 10 May 2026 17:03:32 +0200 Subject: [PATCH 07/24] Reshape ZCP workflow guide --- apps/docs/content/features/coding-agents.mdx | 16 +- apps/docs/content/zcp/overview.mdx | 18 +- .../content/zcp/reference/mcp-operations.mdx | 74 +++--- .../content/zcp/reference/troubleshooting.mdx | 96 +++---- .../zcp/security/production-policy.mdx | 18 +- .../security/tokens-and-project-access.mdx | 8 +- .../content/zcp/setup/hosted-workspace.mdx | 15 +- .../content/zcp/setup/local-agent-bridge.mdx | 2 +- .../zcp/workflows/build-and-verify-app.mdx | 97 +------ .../content/zcp/workflows/build-with-zcp.mdx | 193 +++++++------- .../workflows/create-or-adopt-services.mdx | 65 +---- .../zcp/workflows/delivery-handoff.mdx | 102 +------- .../zcp/workflows/package-running-service.mdx | 10 +- apps/docs/sidebars.js | 24 +- apps/docs/src/css/custom.css | 246 +++++++++++++++++- 15 files changed, 483 insertions(+), 501 deletions(-) diff --git a/apps/docs/content/features/coding-agents.mdx b/apps/docs/content/features/coding-agents.mdx index 37bec9ad7..f3e1c9468 100644 --- a/apps/docs/content/features/coding-agents.mdx +++ b/apps/docs/content/features/coding-agents.mdx @@ -23,14 +23,14 @@ The **Include Coding Agent** option currently bundles Claude Code and wires it t ZCP exposes a fixed set of operations on one Zerops project, grouped by user job. The prompt can stay focused on the app because the agent does not need you to describe what the project looks like - ZCP reports it from what is 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 | [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) | -| **Verify** | Reachability + behavior checks against the actual URL | [Deploy, verify, and fix](/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) | -| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | +| 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 with ZCP](/zcp/workflows/build-with-zcp#develop-with-live-project-context) | +| **Verify** | Reachability + behavior checks against the actual URL | [How ZCP works](/zcp/concept/how-it-works#verification-has-two-layers) | +| **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) | +| **Handoff** | Decide how finished work ships — direct, git, or external CI | [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | Behind one app request, the agent should read the project, use existing services or create missing ones, edit and deploy the app, verify the requested behavior, and then record how future changes should ship. A green deploy with a 500 on the relevant route is not done — verification has two layers and both must pass. diff --git a/apps/docs/content/zcp/overview.mdx b/apps/docs/content/zcp/overview.mdx index 57c7bc082..0ca0b2125 100644 --- a/apps/docs/content/zcp/overview.mdx +++ b/apps/docs/content/zcp/overview.mdx @@ -74,15 +74,15 @@ First-read path: Specific tasks and reference: -| If you want to | Read | -| ---------------------------------------- | ----------------------------------------------------------------- | -| Use remote workspace in a project | [Use remote ZCP workspace](/zcp/setup/hosted-workspace) | -| Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | -| Decide how finished work ships | [Choose how finished work ships](/zcp/workflows/delivery-handoff) | -| Understand tokens and project boundaries | [Trust model](/zcp/security/trust-model) | -| Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | -| Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | -| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | +| If you want to | Read | +| ---------------------------------------- | --------------------------------------------------------------------------- | +| Use remote workspace in a project | [Use remote ZCP workspace](/zcp/setup/hosted-workspace) | +| Use your local editor or CLI agent | [Set up local ZCP](/zcp/setup/local-agent-bridge) | +| Decide how finished work ships | [Build with ZCP](/zcp/workflows/build-with-zcp#choose-delivery-after-proof) | +| Understand tokens and project boundaries | [Trust model](/zcp/security/trust-model) | +| Understand why ZCP exists | [ZCP for coding agents](/features/coding-agents) | +| Look up exact workflow terms | [Workflow terms](/zcp/reference/agent-workflow) | +| Diagnose a stuck session | [Troubleshooting](/zcp/reference/troubleshooting) | ## What stays outside ZCP diff --git a/apps/docs/content/zcp/reference/mcp-operations.mdx b/apps/docs/content/zcp/reference/mcp-operations.mdx index 4be9ecb0a..2c88a6474 100644 --- a/apps/docs/content/zcp/reference/mcp-operations.mdx +++ b/apps/docs/content/zcp/reference/mcp-operations.mdx @@ -1,59 +1,59 @@ --- -title: "Advanced operations" -description: "Common ZCP operation names for app-building, debugging, and automation." +title: 'Advanced operations' +description: 'Common ZCP operation names for app-building, debugging, and automation.' --- Most users never need operation names. They matter when you configure agent-client policy, debug an MCP integration, or automate ZCP directly. In normal app work, ask the agent for outcomes. Recipe-authoring and ZCP-internal maintenance operations are intentionally out of scope. ## Permission classes -| Class | Meaning | -|---|---| -| Read-only | Inspect state, logs, events, guidance, or verification output. | -| Mutating | Change services, env vars, scaling, deploy state, or lifecycle. | +| Class | Meaning | +| ----------------- | ----------------------------------------------------------------------------------------- | +| Read-only | Inspect state, logs, events, guidance, or verification output. | +| Mutating | Change services, env vars, scaling, deploy state, or lifecycle. | | Operational setup | Coordinate workflows, mounts, imports, recipes, delivery setup, or generated local files. | ## Read-only operations -| Operation | Purpose | -|---|---| -| `zerops_discover` | Read services, ports, env-var keys, and current state. | -| `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | -| `zerops_events` | Read service-scoped project activity, deploys, builds, scaling, and failures. | -| `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | -| `zerops_knowledge` | Fetch ZCP guidance or platform knowledge for the current state. | -| `zerops_process` | Check a known async process; cancel is a mutating action. | +| Operation | Purpose | +| ------------------ | ----------------------------------------------------------------------------- | +| `zerops_discover` | Read services, ports, env-var keys, and current state. | +| `zerops_logs` | Read runtime/build logs filtered by service, severity, time, or search. | +| `zerops_events` | Read service-scoped project activity, deploys, builds, scaling, and failures. | +| `zerops_verify` | Run service health, recent error log, and HTTP-readiness checks. | +| `zerops_knowledge` | Fetch ZCP guidance or platform knowledge for the current state. | +| `zerops_process` | Check a known async process; cancel is a mutating action. | ## Mutating operations -| Operation | Purpose | -|---|---| -| `zerops_deploy` | Ship code through the Zerops build and deploy pipeline. | -| `zerops_env` | Read, set, delete, or generate env vars and local `.env` files. | -| `zerops_manage` | Start, stop, restart, reload, or connect storage. | -| `zerops_scale` | Change CPU, RAM, disk, CPU mode, or container autoscaling where supported. `HA`/`NON_HA` is set at service creation. | -| `zerops_subdomain` | Enable or disable public subdomain access. | -| `zerops_delete` | Delete a service. Explicit named approval is required. | +| Operation | Purpose | +| ------------------ | -------------------------------------------------------------------------------------------------------------------- | +| `zerops_deploy` | Ship code through the Zerops build and deploy pipeline. | +| `zerops_env` | Read, set, delete, or generate env vars and local `.env` files. | +| `zerops_manage` | Start, stop, restart, reload, or connect storage. | +| `zerops_scale` | Change CPU, RAM, disk, CPU mode, or container autoscaling where supported. `HA`/`NON_HA` is set at service creation. | +| `zerops_subdomain` | Enable or disable public subdomain access. | +| `zerops_delete` | Delete a service. Explicit named approval is required. | ## Operational setup -| Operation | Purpose | -|---|---| -| `zerops_workflow` | Track, recover, or configure ZCP work sessions; also carries the package-running-service export flow. | -| `zerops_import` | Import project/service definitions. Destructive override is gated. | -| `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | -| `zerops_preprocess` | Expand Zerops preprocessor expressions. | -| `zerops_export` | Read platform project/service export YAML and service metadata. | +| Operation | Purpose | +| ------------------- | ----------------------------------------------------------------------------------------------------- | +| `zerops_workflow` | Track, recover, or configure ZCP work sessions; also carries the package-running-service export flow. | +| `zerops_import` | Import project/service definitions. Destructive override is gated. | +| `zerops_mount` | Mount or unmount runtime filesystems in remote setup. | +| `zerops_preprocess` | Expand Zerops preprocessor expressions. | +| `zerops_export` | Read platform project/service export YAML and service metadata. | ## Remote and local differences -| Area | Remote setup | Local setup | -|---|---|---| -| Files | Runtime files through SSHFS/project containers | Local working directory | -| Deploy source | Project service or batch deploy inside Zerops | Local `workingDir` | -| Dev server | ZCP can run/inspect project-container dev processes | Your local tool owns the dev server | -| Env bridge | Project env is available in the project | `.env` generation resolves project references for local app use | -| Git | Workspace-managed credentials | User's local git credentials | +| Area | Remote setup | Local setup | +| ------------- | --------------------------------------------------- | --------------------------------------------------------------- | +| Files | Runtime files through SSHFS/project containers | Local working directory | +| Deploy source | Project service or batch deploy inside Zerops | Local `workingDir` | +| Dev server | ZCP can run/inspect project-container dev processes | Your local tool owns the dev server | +| Env bridge | Project env is available in the project | `.env` generation resolves project references for local app use | +| Git | Workspace-managed credentials | User's local git credentials | ## Confirmation gates @@ -62,4 +62,4 @@ Two operations require explicit care: - **Service deletion** requires explicit user approval in the current conversation, by service name. - **Destructive import override** first refuses and names what would be replaced; a second call must acknowledge the same targets. -See [Tokens and credentials](/zcp/security/tokens-and-project-access#confirmation-gates-for-destructive-actions) for the user-facing confirmation flow. +See [Tokens and credentials](/zcp/security/tokens-and-project-access#what-zcp-enforces-for-destructive-actions) for the user-facing confirmation flow. diff --git a/apps/docs/content/zcp/reference/troubleshooting.mdx b/apps/docs/content/zcp/reference/troubleshooting.mdx index d6877805e..2062601d7 100644 --- a/apps/docs/content/zcp/reference/troubleshooting.mdx +++ b/apps/docs/content/zcp/reference/troubleshooting.mdx @@ -1,6 +1,6 @@ --- -title: "Troubleshooting" -description: "Recover ZCP sessions by reading current state, failure category, logs, events, token state, VPN state, and local setup artifacts." +title: 'Troubleshooting' +description: 'Recover ZCP sessions by reading current state, failure category, logs, events, token state, VPN state, and local setup artifacts.' --- Start recovery from current project state, not from chat memory. @@ -15,28 +15,28 @@ That prompt fits confused sessions, interrupted browser tabs or CLI sessions, re When a deploy or verify step fails, ZCP should surface a category with the likely cause and next move. Categories are recovery-shaped. `network` does not mean "the network is broken"; it means the first useful move is connectivity, VPN, SSH, DNS, or public-route evidence rather than editing app code. -| Category | Meaning | Read first | Recovery move | -|---|---|---|---| -| `build` | Build failed before runtime start. | Build logs and `zerops.yaml` build steps. | Fix build commands, dependency manifests, lock files, deploy files, or build resources. Runtime logs will not explain this failure. | -| `start` | Build passed, runtime failed to start or crashed. | Prepare logs or runtime logs, whichever the failure names. | Check `run.start`, ports, env references, `prepareCommands`, and whether the process binds `0.0.0.0` rather than `127.0.0.1`. | -| `verify` | Runtime exists, but reachability or requested behavior failed. | The failing check, HTTP response, and runtime logs at request time. | Treat it as app behavior or route recovery, not deploy success. | -| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | The transport error and the named network path. | Check VPN for local setup, service status for remote setup, SSH transfer state, DNS/subdomain readiness, or platform timeout. | -| `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Structured rejection detail, field path, or API metadata. | Fix the named setup block, env reference, `deployFiles`, service type, or prerequisite. | -| `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | The credential surface named in the error. | Replace that credential only: `ZCP_API_KEY`, `GIT_TOKEN`, local git auth, `ZEROPS_TOKEN`, SSH key, or app secret. | -| `other` | No known category matched. | Raw reason, service events, and logs for the named phase. | Stop if the same unknown reason repeats; report the raw reason and recent evidence. | +| Category | Meaning | Read first | Recovery move | +| ------------ | --------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `build` | Build failed before runtime start. | Build logs and `zerops.yaml` build steps. | Fix build commands, dependency manifests, lock files, deploy files, or build resources. Runtime logs will not explain this failure. | +| `start` | Build passed, runtime failed to start or crashed. | Prepare logs or runtime logs, whichever the failure names. | Check `run.start`, ports, env references, `prepareCommands`, and whether the process binds `0.0.0.0` rather than `127.0.0.1`. | +| `verify` | Runtime exists, but reachability or requested behavior failed. | The failing check, HTTP response, and runtime logs at request time. | Treat it as app behavior or route recovery, not deploy success. | +| `network` | Transport, DNS, VPN, SSH, or service-to-service reach failed. | The transport error and the named network path. | Check VPN for local setup, service status for remote setup, SSH transfer state, DNS/subdomain readiness, or platform timeout. | +| `config` | `zerops.yaml`, setup, env vars, or service settings mismatch. | Structured rejection detail, field path, or API metadata. | Fix the named setup block, env reference, `deployFiles`, service type, or prerequisite. | +| `credential` | Zerops, git, SSH, managed-service, or external API auth failed. | The credential surface named in the error. | Replace that credential only: `ZCP_API_KEY`, `GIT_TOKEN`, local git auth, `ZEROPS_TOKEN`, SSH key, or app secret. | +| `other` | No known category matched. | Raw reason, service events, and logs for the named phase. | Stop if the same unknown reason repeats; report the raw reason and recent evidence. | Build logs and runtime logs are different streams. A build failure shows in the build container; a runtime crash shows in runtime logs. If the agent reads the wrong stream, the diagnosis will be wrong even when the log command succeeded. ## Deploy and start symptoms -| Symptom | Likely category | Next move | -|---|---|---| -| Build exits with "command not found", missing module, npm 404, or compile error | `build` | Read `buildCommands`, manifests, lock files, package manager, and deploy file list. | -| Build or SSH transfer ends with `signal: killed` | `build` / `network` | Treat it as memory pressure first: scale build/source resources or reduce the heavy step. | -| Runtime crashes immediately with `EADDRINUSE`, missing module, or missing env var | `start` | Read runtime logs scoped to the failed start and check the start command/env contract. | -| Database connection refused during init/start | `start` / `network` | Confirm the managed service is running and env references such as `${db_*}` resolve. | -| Preflight refuses `INVALID_ZEROPS_YML` or setup mismatch | `config` | Read the field-level rejection and fix that setup entry; setup names are not always hostnames. | -| Git push returns authentication failed | `credential` | Remote setup needs `GIT_TOKEN`; local setup uses your local git credentials. | +| Symptom | Likely category | Next move | +| --------------------------------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------- | +| Build exits with "command not found", missing module, npm 404, or compile error | `build` | Read `buildCommands`, manifests, lock files, package manager, and deploy file list. | +| Build or SSH transfer ends with `signal: killed` | `build` / `network` | Treat it as memory pressure first: scale build/source resources or reduce the heavy step. | +| Runtime crashes immediately with `EADDRINUSE`, missing module, or missing env var | `start` | Read runtime logs scoped to the failed start and check the start command/env contract. | +| Database connection refused during init/start | `start` / `network` | Confirm the managed service is running and env references such as `${db_*}` resolve. | +| Preflight refuses `INVALID_ZEROPS_YML` or setup mismatch | `config` | Read the field-level rejection and fix that setup entry; setup names are not always hostnames. | +| Git push returns authentication failed | `credential` | Remote setup needs `GIT_TOKEN`; local setup uses your local git credentials. | Useful prompt: @@ -48,13 +48,13 @@ Show me the failure category, build logs, runtime logs, and recent events for th A deploy can be green and still fail verification. Verify has two layers: platform reachability, then the requested behavior. -| Symptom | Likely cause | Next move | -|---|---|---| -| Service is `RUNNING`, but the route returns 500 or 404 | App route, dependency, or request handling is broken. | Read runtime logs at request time and verify the requested route/body, not only `/`. | -| Public URL returns connection error right after first deploy | Subdomain route is still propagating, or the runtime is not bound correctly. | Wait 30-60 seconds and retry; if it persists, check port binding and public-access eligibility. | -| 502 persists after deploy and wait | Start command binds `127.0.0.1`, wrong port, or HTTP server not listening. | Check `zerops.yaml` `run.ports[]` and make the app bind the same port on `0.0.0.0`. | -| Subdomain stays disabled | Worker or non-HTTP service, or explicit public access missing. | Workers do not get useful public URLs. If it is an HTTP runtime, ask the agent to enable subdomain access and verify it. | -| Requested feature fails after a successful deploy | Behavior verification failed. | Continue the loop from logs/events/check output; do not report the task done. | +| Symptom | Likely cause | Next move | +| ------------------------------------------------------------ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| Service is `RUNNING`, but the route returns 500 or 404 | App route, dependency, or request handling is broken. | Read runtime logs at request time and verify the requested route/body, not only `/`. | +| Public URL returns connection error right after first deploy | Subdomain route is still propagating, or the runtime is not bound correctly. | Wait 30-60 seconds and retry; if it persists, check port binding and public-access eligibility. | +| 502 persists after deploy and wait | Start command binds `127.0.0.1`, wrong port, or HTTP server not listening. | Check `zerops.yaml` `run.ports[]` and make the app bind the same port on `0.0.0.0`. | +| Subdomain stays disabled | Worker or non-HTTP service, or explicit public access missing. | Workers do not get useful public URLs. If it is an HTTP runtime, ask the agent to enable subdomain access and verify it. | +| Requested feature fails after a successful deploy | Behavior verification failed. | Continue the loop from logs/events/check output; do not report the task done. | Public subdomain access is enabled on first deploy for eligible HTTP runtimes. Workers and non-HTTP services do not get a useful public URL by design. @@ -62,13 +62,13 @@ Public subdomain access is enabled on first deploy for eligible HTTP runtimes. W Token problems usually happen at startup. The fix is to replace the token on the surface ZCP reads, then restart the agent so the new process inherits it. -| Symptom | Likely cause | Next move | -|---|---|---| -| `Token accesses N projects; use project-scoped token` | Multi-project or account-wide token. | Generate a token scoped to exactly one project and replace `ZCP_API_KEY`. | -| `Token has no project access` | Token authenticates but reaches no project. | Grant project access or generate a new project-scoped token. | -| `No authentication found: set ZCP_API_KEY or log in with zcli` | No token reached ZCP. | Add `ZCP_API_KEY` under `.mcp.json` `env` for local setup, or check remote service env. | -| API replies with 401 or `AUTH_TOKEN_EXPIRED` | Token expired or was revoked. | Generate a new project-scoped token, replace it, restart the agent. | -| GitHub Actions deploy fails on auth | `ZEROPS_TOKEN` missing/wrong. | Set a Zerops API token as the repository secret named `ZEROPS_TOKEN`. It is not a GitHub token. | +| Symptom | Likely cause | Next move | +| -------------------------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `Token accesses N projects; use project-scoped token` | Multi-project or account-wide token. | Generate a token scoped to exactly one project and replace `ZCP_API_KEY`. | +| `Token has no project access` | Token authenticates but reaches no project. | Grant project access or generate a new project-scoped token. | +| `No authentication found: set ZCP_API_KEY or log in with zcli` | No token reached ZCP. | Add `ZCP_API_KEY` under `.mcp.json` `env` for local setup, or check remote service env. | +| API replies with 401 or `AUTH_TOKEN_EXPIRED` | Token expired or was revoked. | Generate a new project-scoped token, replace it, restart the agent. | +| GitHub Actions deploy fails on auth | `ZEROPS_TOKEN` missing/wrong. | Set a Zerops API token as the repository secret named `ZEROPS_TOKEN`. It is not a GitHub token. | Full credential model: [Tokens and credentials](/zcp/security/tokens-and-project-access). @@ -76,14 +76,14 @@ Full credential model: [Tokens and credentials](/zcp/security/tokens-and-project These apply to local setup. Remote setup is already on the project-private network and does not need VPN from your laptop. -| Symptom | Likely cause | Next move | -|---|---|---| -| Local app cannot reach `db`, `redis`, `cache`, or storage hostname | VPN is down or dropped after sleep/network change. | Run `zcli vpn up ` again. | -| `no route to host` or connection refused for managed-service hostname | Same network path issue. | Bring VPN up, then retry. If network works, regenerate `.env`. | -| Local app reads stale project credentials | `.env` is a snapshot. | Ask ZCP to regenerate the `.env` after project env changes. | -| Re-running `zcp init` made ZCP disappear from the agent | `.mcp.json` was rewritten without `ZCP_API_KEY`. | Re-add the `env` block and restart the agent from the project root. | -| Agent does not list the `zerops` MCP server | Agent launched from the wrong directory or config not loaded. | Quit and relaunch from the directory containing `.mcp.json`. | -| `zcp` is not found after install | Install path is not on `PATH`. | Add `~/.local/bin` or the install target to your shell `PATH`, then restart the shell/agent. | +| Symptom | Likely cause | Next move | +| --------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | +| Local app cannot reach `db`, `redis`, `cache`, or storage hostname | VPN is down or dropped after sleep/network change. | Run `zcli vpn up ` again. | +| `no route to host` or connection refused for managed-service hostname | Same network path issue. | Bring VPN up, then retry. If network works, regenerate `.env`. | +| Local app reads stale project credentials | `.env` is a snapshot. | Ask ZCP to regenerate the `.env` after project env changes. | +| Re-running `zcp init` made ZCP disappear from the agent | `.mcp.json` was rewritten without `ZCP_API_KEY`. | Re-add the `env` block and restart the agent from the project root. | +| Agent does not list the `zerops` MCP server | Agent launched from the wrong directory or config not loaded. | Quit and relaunch from the directory containing `.mcp.json`. | +| `zcp` is not found after install | Install path is not on `PATH`. | Add `~/.local/bin` or the install target to your shell `PATH`, then restart the shell/agent. | ## Session drift and manual takeover @@ -97,12 +97,12 @@ That is the right move when the agent says it has no context, a browser workspac For manual takeover, read evidence in this order: -| Evidence | Why it matters | -|---|---| -| Service-scoped events | Shows deploys, build/start transitions, failed process reasons, scaling, lifecycle actions. | -| Build logs and runtime logs | Separates build failures from runtime crashes and request-time app errors. | -| Verify output | Shows whether reachability and requested behavior actually passed. | -| Git history | Matters when delivery uses git-push; shows what source change corresponds to the deployed result. | +| Evidence | Why it matters | +| --------------------------- | ------------------------------------------------------------------------------------------------- | +| Service-scoped events | Shows deploys, build/start transitions, failed process reasons, scaling, lifecycle actions. | +| Build logs and runtime logs | Separates build failures from runtime crashes and request-time app errors. | +| Verify output | Shows whether reachability and requested behavior actually passed. | +| Git history | Matters when delivery uses git-push; shows what source change corresponds to the deployed result. | ## When to stop @@ -114,7 +114,7 @@ Before destructive recovery, read service-scoped events, logs, deploy/verify res ## Related -- [Deploy, verify, and fix](/zcp/workflows/build-and-verify-app) +- [Build with ZCP](/zcp/workflows/build-with-zcp) - [Workflow terms](/zcp/reference/agent-workflow) - [Tokens and credentials](/zcp/security/tokens-and-project-access) - [Set up local ZCP](/zcp/setup/local-agent-bridge) diff --git a/apps/docs/content/zcp/security/production-policy.mdx b/apps/docs/content/zcp/security/production-policy.mdx index 6b19576b4..c75f2b889 100644 --- a/apps/docs/content/zcp/security/production-policy.mdx +++ b/apps/docs/content/zcp/security/production-policy.mdx @@ -62,15 +62,15 @@ Broader CI/CD reference: [CI/CD with Zerops](/guides/ci-cd). If a production deploy fails, investigate in the production project with production logs, events, backups, and CI output. Do not attach the development agent directly to production as a shortcut. -## What not to do +## Production separation rules -| Anti-pattern | Why it breaks the boundary | -| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | -| Add a `zcp` service to the production project | Puts a coding agent inside production with project-scoped operational access. | -| Put a production token in local `.mcp.json` | Lets the local agent operate production from your machine. | -| Store production `ZEROPS_TOKEN` in the development `zcp` service | Lets development tooling deploy to production outside the release path. | -| Treat dev verification as production approval | Dev can use hot-reload, mutable data, or cheaper services. Stage is the rehearsal; production approval is separate. | -| Let the agent deploy directly from a development session into production | Bypasses the release evidence and credential boundary. | +| Rule | Why it matters | +| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------- | +| Keep the production project without a `zcp` service | Production should not host an agent workspace with project-scoped operational access. | +| Keep production tokens out of local `.mcp.json` | Local agents should operate development or staging projects, not production. | +| Keep production `ZEROPS_TOKEN` in the release secret store | Development tooling should not deploy to production outside the release path. | +| Treat stage proof as release evidence, not production approval | Stage is the rehearsal; production approval is a separate release decision. | +| Promote through CI, release tooling, or a deliberate human action | Production execution should use the production credential boundary. | ## Acceptable agent involvement @@ -82,7 +82,7 @@ The agent can still help before production promotion: - push or prepare the code path your team uses for review, - summarize release notes and known blockers for the human or CI handoff. -Stop there. Production execution belongs to the production release path. +ZCP's involvement stops at the handoff. Production execution belongs to the production release path. ## Next steps 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 d7899d80e..eb711e37c 100644 --- a/apps/docs/content/zcp/security/tokens-and-project-access.mdx +++ b/apps/docs/content/zcp/security/tokens-and-project-access.mdx @@ -117,7 +117,7 @@ Rotate in the Zerops dashboard, then update the surface that consumes the token. Rotation is picked up on next process start or workflow run, not in the middle of a live agent session. -## Confirmation gates for destructive actions +## What ZCP enforces for destructive actions A valid token does not remove every guardrail. ZCP adds explicit confirmation for operations where the loss is not safely reversible from inside the conversation. @@ -130,17 +130,17 @@ Deploys, env changes, lifecycle actions, restarts, scaling, and public-access ch Approval from a previous chat does not carry forward. A new conversation needs a new approval. -## Diagnose before destruct +## Evidence before destructive recovery When a service has recent failure history, ZCP enforces one recovery rule: read the platform evidence before destroying or replacing the service. -The agent should inspect service events, build logs, runtime logs, and failure summaries, then either fix the cause or show you the evidence before asking for destructive confirmation. This prevents delete-and-retry loops that erase the failure context the next session needs. +The agent should inspect service events, build logs, runtime logs, and failure summaries, then either fix the cause or show you the evidence before asking for destructive confirmation. The point is to preserve the failure context the next session needs. A service waiting for first code deploy is not the same thing as a failed service. The gate is about recorded failure history, not idle state. Threat model and boundaries: [Trust model](/zcp/security/trust-model). -## Gotchas +## Credential checks - **Account-wide full-access tokens are refused.** ZCP needs one project, not a broad account credential. - **`zcp init` regenerates `.mcp.json`.** Re-add `ZCP_API_KEY` after rerunning it. diff --git a/apps/docs/content/zcp/setup/hosted-workspace.mdx b/apps/docs/content/zcp/setup/hosted-workspace.mdx index 2d001ccbf..24ae87cf1 100644 --- a/apps/docs/content/zcp/setup/hosted-workspace.mdx +++ b/apps/docs/content/zcp/setup/hosted-workspace.mdx @@ -118,14 +118,17 @@ Use these patterns: - **Team-standard workspace:** build a derived image based on `zcp@1` with required tools already present. - **Helper processes:** run project-local helpers next to the agent and editor workspace when your team needs private integrations. -Avoid putting app runtime build steps into the `zcp` service. Runtime services own app builds and deploys. +Keep app runtime build steps with the runtime services. The remote workspace carries the agent, tools, and project access; runtime services own app builds and deploys. -## Boundaries to keep +## What belongs where -- **The `zcp` service is not the app.** Deploy app code to runtime services, not to the workspace service. -- **Do not set `ZCP_API_KEY` manually in remote setup.** Zerops injects it. A manual override can break the project boundary. -- **The agent account is separate from Zerops.** If Claude Code asks you to sign in, use your Claude subscription or model API credential. -- **Remote setup is not production policy.** Use it in development or staging projects. Production should live in a separate project without a `zcp` service; see [Production boundary](/zcp/security/production-policy). +| Concern | Where it belongs | +| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Agent workspace | The `zcp@1` service, with Claude Code, browser VS Code, shell tools, MCP servers, and team-specific helpers. | +| App code deploys | Runtime services such as `appdev`, `appstage`, or `app`; not the workspace service. | +| ZCP project token | Injected by Zerops into the remote workspace. You normally do not set `ZCP_API_KEY` by hand in remote setup. | +| Agent account | Your Claude subscription login or model API credential. Zerops wires the agent to ZCP, but the agent account remains yours. | +| Production promotion | A separate production project and release path. Remote setup belongs in development or staging projects; see [Production boundary](/zcp/security/production-policy). | ## Next steps diff --git a/apps/docs/content/zcp/setup/local-agent-bridge.mdx b/apps/docs/content/zcp/setup/local-agent-bridge.mdx index 4ff204835..3ce7badff 100644 --- a/apps/docs/content/zcp/setup/local-agent-bridge.mdx +++ b/apps/docs/content/zcp/setup/local-agent-bridge.mdx @@ -167,7 +167,7 @@ ZCP does not mount Zerops runtime filesystems on your laptop. Remote setup can m ZCP does not own your git credentials. In local setup, your local git CLI, SSH agent, or credential helper handles pushes. -## Gotchas +## Local setup checks - **Launch directory matters.** Start the agent from the folder that contains `.mcp.json`; otherwise it may connect to no ZCP server or the wrong project. - **Multi-project tokens are rejected.** Use a token scoped to exactly one project. 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 15f0217e3..af5a127d3 100644 --- a/apps/docs/content/zcp/workflows/build-and-verify-app.mdx +++ b/apps/docs/content/zcp/workflows/build-and-verify-app.mdx @@ -1,99 +1,10 @@ --- title: 'Deploy, verify, and fix' -description: 'How the agent changes app code, deploys through Zerops, reads evidence, fixes failures, and proves behavior.' +description: 'Development and verification are now covered in Build with ZCP.' --- -After the target runtime and dependencies are known, the agent can change code and `zerops.yaml`, deploy the runtime, fix failures from evidence, and verify the result. +Development, deploy, verification, and evidence-driven fixes are part of the normal ZCP development lifecycle. The user-facing development controls live in [Build with ZCP → Develop with live project context](/zcp/workflows/build-with-zcp#develop-with-live-project-context). The deeper verification model lives in [How ZCP works → Verification has two layers](/zcp/concept/how-it-works#verification-has-two-layers). -A ZCP app task is done only when the target runtime was deployed, platform reachability passed, the requested behavior was verified against the real endpoint or UI, and the agent reported the working URL or a concrete blocker. +Those sections explain how the agent works from live project state, Zerops-specific knowledge, project-scoped tools, deploy evidence, and behavior proof. -Localhost is not delivery. A local dev server can speed up implementation, but the completion proof comes from the Zerops runtime or the linked deploy target. - -## The expected loop - -ZCP treats deploy as diagnostics and verification as the completion gate. You do not have to script this sequence; it is the expected path behind an app prompt. - -- **Runtime scope.** The agent identifies which runtime changed. -- **Code and `zerops.yaml`.** The agent changes app files and runtime config where needed. -- **First deploy.** The first verified runtime deploy goes through ZCP. -- **Runtime reachability.** The agent checks that the runtime answers on the real platform path. -- **Requested behavior.** The agent proves the endpoint, URL, UI state, row, job result, or other acceptance evidence. -- **Evidence-based fix.** If either verification layer fails, the agent reads new logs/events/check output, fixes the cause, and redeploys. - -A green build or running runtime is not done until the requested endpoint, UI, or state is checked on the real URL. Retries should use new evidence, not repeat the same deploy. - -## Runtime scope is a precondition - -Before editing, the agent should name the runtime service that will run app code: for example `appdev`, `appstage`, `app`, or the linked runtime in a local setup. Managed services such as databases and caches are dependencies, not deploy targets. - -If the agent starts writing files before it knows the runtime scope, treat that as a correction signal: it should read ZCP status and identify the target before continuing. - -## Deploy - -The first deploy uses ZCP's direct deploy path so the agent can produce and verify a real running result. Delivery choices such as git or external handoff apply after that verified result exists. - -A direct deploy waits for the Zerops build/deploy process to finish and for the runtime to be ready enough to verify. If finished work later ships through git, the push and the follow-up build are separate from this first proof step. See [Choose how finished work ships](/zcp/workflows/delivery-handoff). - -Build and runtime evidence are separate: - -- Build runs in a temporary build container. Build logs explain compile errors, missing packages, lock-file problems, and failed build commands. -- Runtime logs explain start crashes, port binding, request-time 500s, missing env vars, and app-level failures. -- Events show deploy/build transitions, service lifecycle, scaling, public access, and failed process reasons. - -Remote setup can deploy multiple hosted runtime services in parallel when the task spans them. Local setup deploys from your working directory to the linked runtime target one service at a time. - -Deploy failures point to different evidence: - -| Failure point | Read first | -| --------------------------------------- | ------------------------------------------------------ | -| Build failed | Build logs and `zerops.yaml` build steps | -| Runtime started then crashed | Runtime logs and start command | -| App is up but route fails | Runtime logs at request time and behavior check output | -| Local app cannot reach managed services | VPN state and generated `.env` values | - -## Verify - -Verification has two layers: - -| Layer | What it proves | -| --------------------- | -------------------------------------------------------------------------------------------------------------- | -| Platform reachability | The service is running, recent error logs are checked, and HTTP runtime services answer a probe when eligible. | -| Requested behavior | The thing you asked for works: endpoint body, UI state, database row, job result, or stage URL. | - -Both layers must pass before the agent reports completion. - -Good final proof sounds specific: - -```text -Deployed appdev, opened https://task-board.zerops.app, created a task, refreshed, and confirmed the task was still present. -``` - -Weak proof sounds generic: - -```text -Build passed and the app is running. -``` - -## Fix or stop - -The agent should name the failure point in plain language, read fresh logs/events/check output, fix the indicated cause, and redeploy. A repeated retry with no new evidence is not progress. - -Use this escalation shape: - -| Stage | What should change | -| ---------------- | -------------------------------------------------------------------------------------------------------------------- | -| First retries | Diagnose from the named logs/events/check output and fix the targeted issue. | -| Repeated failure | Re-check `zerops.yaml`, env vars, `deployFiles`, build commands, start command, and health/public route assumptions. | -| No progress | Stop and ask. The loop likely needs a missing decision, credential, or human diagnosis. | - -Stop sooner when scope is ambiguous, credentials are missing, destructive recovery is needed, or several evidence-based retries hit the same failure. - -## Review and take over - -The platform records deploys, events, runtime logs, and lifecycle actions. It does not record every agent thought, browser click, or shell edit. When taking over, ask for ZCP status first, then read service-scoped events, logs, deploy/verify results, and git history if delivery uses git. - -## Gotchas - -1. **Deploy success is not verify success.** A green build with a broken feature is not complete. -2. **Stage is explicit.** In dev+stage projects, the agent should say when stage is included before changing or verifying it. -3. **Secrets belong in env vars.** If the app needs a third-party API key, the agent should ask for an env-var path, not put secrets in source. +For symptoms and recovery moves, use [Troubleshooting](/zcp/reference/troubleshooting). diff --git a/apps/docs/content/zcp/workflows/build-with-zcp.mdx b/apps/docs/content/zcp/workflows/build-with-zcp.mdx index d67e2f361..3acae6916 100644 --- a/apps/docs/content/zcp/workflows/build-with-zcp.mdx +++ b/apps/docs/content/zcp/workflows/build-with-zcp.mdx @@ -1,101 +1,120 @@ --- title: 'Build with ZCP' -description: 'How a product prompt turns into service setup, app work, verification, and delivery after proof.' +description: 'The practical ZCP development surface: runtime layout, live project context, and delivery preference.' --- -Start with what the app should do, not with tool calls or workflow names. +Build with ZCP starts from product intent, not from an operations checklist. ```text -Build a task board where tasks stay saved after refresh. +Build a task board. +Tasks should stay saved after refresh. ``` -Behind that prompt, the agent should inspect the project, choose the app runtime, make the change, deploy it, and use evidence from the real Zerops project until the requested behavior is proven. ZCP supplies the live service map, env state, deploy access, logs, events, verification checks, and guardrails needed for that loop. - -Do not make the prompt longer to restate completion expectations such as deploy, verification, or returning the URL. The words you add should change the app, the stack, the runtime layout, the acceptance criteria, or the delivery path. - -You also do not need to paste an infrastructure inventory, env wiring plan, or log summary into the prompt. ZCP makes those available to the agent from the live project. - -## The three parts of the work - -The useful mental model is: - -1. **Infrastructure setup.** Find or create the runtime and dependencies the app needs. -2. **Development and verification.** Change the app, deploy it, and prove the requested behavior on Zerops. -3. **Delivery after proof.** Decide what happens to the finished work after a verified result exists. - -ZCP makes those boundaries explicit enough that the agent can resume, recover, and stop at the right point without turning your prompt into an operations script. - -### 1. Infrastructure setup - -The agent first reads what exists now: runtime services, managed services, env vars, recent events, and current work state. Then it establishes the app target: - -- use existing runtimes and managed services when they fit, -- create missing runtimes or managed dependencies when the request needs them, -- identify the app runtime that will receive code, -- keep `zcp` as the workspace/control-plane service, not as the app runtime. - -This phase ends when the app runtime and dependencies are known. It should not end with "the app is done"; no application behavior has been proven yet. - -Useful prompts: - -```text -Build a Node.js API with PostgreSQL. Use the existing dev+stage pair if this project has one. -``` - -```text -Use the existing Laravel services and add a small notes app. -``` - -### 2. Development and verification - -After the target is known, the agent changes app files and `zerops.yaml`, deploys through Zerops, verifies platform reachability, verifies the requested behavior, reads evidence on failure, and retries from the cause. - -Two checks matter: - -| Check | What it answers | -| ------------------ | -------------------------------------------------------------------------------------------------- | -| Reachability | Did the runtime build, start, and answer on the real platform path? | -| Requested behavior | Does the app do the thing you asked for on the real URL, endpoint, worker result, or stored state? | - -A successful build is not enough. A page returning 200 is not enough when the prompt asked for saved tasks. The agent should report the behavior it checked, not only the deploy it ran. - -If the agent appears to drift, correct it in terms of the outcome you need: - -| If the agent... | Ask it to... | -| --------------------------------------------- | -------------------------------------------------------------------- | -| writes files before naming the runtime target | read ZCP status and identify the app runtime first | -| targets the `zcp` service as the app | switch to the runtime service that runs app code | -| repeats deploys without new evidence | read fresh logs, events, or verify output and name the failure point | -| reports completion after only a green build | verify the requested behavior on the real URL or endpoint | - -### 3. Delivery after proof - -After a verified deploy exists, choose what should happen to the finished work and future changes: - -| Choice | Use when | -| ------------------ | ------------------------------------------------------------------------ | -| Keep direct deploy | Fast iteration, demos, early development, or agent-owned dev/stage work. | -| Push to git | You want commits, repository history, review, or repo-driven builds. | -| External handoff | CI, release process, or a human owns delivery from here. | - -You can include this intent in the original prompt, but it applies after proof: - -```text -Build a task board where tasks stay saved after refresh. After it verifies, push the result to git@github.com:my-org/task-dashboard.git. -``` - -The first functional deploy still happens through ZCP so the agent can prove the app in the target project. Delivery choice controls what follows a verified result. - -Advanced export and reuse path: [Package a running service](/zcp/workflows/package-running-service). +Behind that prompt, ZCP gives the agent a development surface for one Zerops project: choose the runtime layout, develop from current project state and Zerops-specific guidance, then follow the delivery preference you choose. + +You do not need to add "deploy it", "verify it", or "send me the URL" to every prompt. Add words when they change the product, stack, runtime layout, acceptance criteria, or delivery path. + +## The lifecycle + +