Start growing

One command. Your project. No arguments.

Clone the repository or unpack the v10.0 release, run the installer, and the harness builds itself into a Google Cloud project you already own. There is no account to create here, no key to paste to us, no tenant to be assigned. When it finishes it prints two URLs and hands them to you. This page is the whole path, including the parts that go wrong.

Before you start

Prerequisites — short list, no surprises

Three things, and Cloud Shell already gives you the fourth.

A Google Cloud project
With a billing account linked. Not "billing enabled at the org level, probably" — linked to this project. Everything the harness creates lands here and bills here. If you want a clean blast radius, make a new project for it; deleting the project deletes the install.
A Google account
The one that will own the console. It becomes the identity Google IAP checks in front of /harness. Prefer an account in the same domain as the project — the org policy discussed below refuses out-of-domain bindings at write time, which is a feature you will meet immediately if you ignore this.
Rights on that project
Enough to enable services and create IAM bindings, Cloud Run services, Firestore, buckets, secrets and KMS keys. Project Owner is the simple answer. Step 0 will tell you exactly what you are missing if you are missing something.
A shell
Cloud Shell already provides gcloud, python3, openssl and curl, which is the entire tool list. That is why we point people at Cloud Shell: it removes the whole category of "works on my laptop" from the install.
Nothing on this list is us. There is no Paracoding account, licence key, telemetry endpoint or call-home in the install. The harness is Apache-2.0 and it runs in your project. What you pay is your Google Cloud bill and your own model plans — see what it costs.

Two routes

Tarball or clone — one word of difference

Both routes install the same bytes. They differ by exactly one word on the command line, and the reason is worth thirty seconds of your time.

Route A — a git clone

Fully reproducible from a public URL, and the one you can copy without checking anything first. Note the leading bash.

gcloud config set project YOUR_PROJECT_ID

git clone https://github.com/paracoding-ai/paracoding-oss.git
cd paracoding-oss

bash install.sh

Route B — the release archive

The archive attached to the v10.0 release keeps the modes from the tree, so the installer arrives executable. Download it from that page, unpack it, and run it from inside.

gcloud config set project YOUR_PROJECT_ID

cd paracoding-oss

./install.sh

Why ./install.sh in one and bash install.sh in the other

Files committed through GitHub's web UI carry no executable bit. Git stores mode 0644 for them, so a clone lands a script you cannot execute directly — ./install.sh returns permission denied, which reads like a security problem and is not one. Invoking the interpreter explicitly (bash install.sh) sidesteps the mode entirely.

The release tarball does not have this problem because the release archives are built deliberately: reproducible, with fixed mtimes, gzip level 9, and modes taken from the tree — so install.sh arrives as 0755 and runs. You may of course also just chmod +x install.sh after a clone. We would rather tell you the mechanism than have you guess at a failure that has nothing to do with your project.

No arguments. Not on either route. The installer reads the active gcloud project and derives everything else. If you find yourself wanting to pass a flag, set the project first and run it again.

The flow

What actually happens when you run it

Read this before you run it, then read the output as it goes past. The installer narrates.

  1. 0

    Step 0 — preflight, and the good failure

    Before anything is created, the installer checks the ground it is about to build on: that a project is actually selected, that a billing account is linked to it, that the tools it needs are on PATH, that the identity you are running as can enable services and write IAM bindings, and that the APIs the harness depends on can be turned on. If a check fails, it stops there and names the exact missing permission — the IAM permission string it needs and could not confirm — rather than letting you discover it four minutes later as an opaque error from a Cloud Run deploy that had no business being attempted. Reporting the cause instead of the symptom is the entire point of step 0. Half-built infrastructure is worse than none.

  2. 1

    Services, identity and org policy

    Required Google Cloud APIs are enabled and service identities are created, each scoped to a role rather than sharing one god account. This is also where constraints/iam.allowedPolicyMemberDomains comes into play: with it in force, an out-of-domain account is refused at binding write, not at request time. A wiki page saying "do not grant access to personal accounts" is a policy. Refusing the write is a control.

  3. 2

    State: Firestore, Cloud Storage, Secret Manager, Cloud KMS

    The backing store for serverless git and the rest of the agent state. Refs, HEAD, config, .git/index, packed-refs and the working tree become Firestore documents; objects go to GCS, each sealed in a PCV1 envelope (magic|epoch|flags|nonce(12)|ciphertext|tag(16), AES-256-GCM, HKDF-SHA256, AAD binding the full GCS object key). KMS holds the key-encapsulation material — X-Wing hybrid PQC, ML-KEM-768 with X25519 — and Secret Manager holds the rest.

  4. 3

    Build the image, deploy two Cloud Run services

    One image, two surfaces, split by a 90-entry route table enforced at build time. A route landing on neither service throws at boot rather than 404-ing later. The console service goes behind Google IAP; the MCP service cannot, because IAP consumes the Authorization header and an MCP client needs it. That is why there are two services rather than one with a path prefix.

  5. 4

    Defaults are written

    install.sh sets PC_AUTO_APPROVE=1, and ships PC_GUARDRAILS=0 and PC_REQUIRE_PASSKEY=0. With auto-approve on, a staged job is KMS-signed and executed in the same call, and the journal records who staged it, the command, and what it returned. Set PC_AUTO_APPROVE=0 and a staged job goes to pending and does not run — not on a timer, not on a retry, not ever.

  6. 5

    It prints two URLs and exits

    The console URL, ending in /harness, and the MCP URL, ending in /mcp. Copy both. They are the whole hand-off.

It does not stop to ask you things. v10.0 runs unattended from kickoff to the two URLs at the end: no passkey registration step, no workstation question, and it no longer refuses to run without an interactive terminal. No prompt remains: step 6d/10 seeds exactly one allowed Google account — the one running the install — and asks for nothing. Pass more up front with --approver-emails a@b.com,c@d.com (or PC_APPROVER_EMAILS= in the environment), or add them later in the console Settings under Allowed accounts. Step 0 is a different thing: it stops, it does not ask. A missing prerequisite gets you the exact permission string and a non-zero exit, not a question.

The output

Two URLs, and they are not interchangeable

The most common first-day mistake is pasting one into the other's client. They are different services with different auth models.

URLForAuthWhat happens if you use the wrong one
.../harness Humans. The console: the chat, the wiki, the lakeview, the journal, strain settings. Google IAP. The ES256 assertion is verified against Google's JWKS with iss, exp and aud pinned. X-Goog-Authenticated-User-Email is never trusted on its own. An MCP client pointed here gets bounced by IAP, which consumes the Authorization header the client needs. It cannot work, by design.
.../mcp Machines. One POST endpoint serving ~55 tools — work items, memory graph, journal, file storage, git, execution, infra, messaging. Full OAuth 2.1. Dual-era: MCP revision 2026-07-28 (stateless, per-request metadata, error codes -32020/-32021/-32022) alongside the 2025-era initialize handshake. Era routing is a pure function of one request's bytes — no connection state, no cache, no clock. Opened in a browser it will not give you a console. It is not a web page.

The harness also ships as an Agent Plugins package (agent-plugins.org), so any client that reads that format can connect without hand-configuration, and each strain publishes an A2A agent card at /agents/{role}/.well-known/agent-card.json.

First run

Connect a client, sign in, start working

Console first, then the MCP endpoint. Ten minutes, most of it browser consent screens.

  1. 1

    Open the console

    Paste the /harness URL into a browser signed in as an allowed account. IAP will run you through Google sign-in. You land on the console; the chat is the Flow Hood, and it is the same surface that built and deployed the services in the screenshots.

  2. 2

    Add the MCP endpoint to your client

    Give your MCP client the /mcp URL as a remote server. Any OAuth 2.1-capable MCP client works; the dual-era endpoint means an older client speaking the 2025-era initialize handshake is served correctly from the same address, with no flag to set and nothing to downgrade.

  3. 3

    The OAuth 2.1 flow

    Your client discovers the authorisation server, opens a browser, you sign in with the same Google account, you consent, the client receives a token and completes the handshake. From that point the client is holding its own credential — nothing is copied into a config file by hand and nothing is shared with us, because there is no us in the path. The token authorises a role, so what that client can do is bounded by the role it authorised as.

  4. 4

    If the first sign-in returns a 401

    It happens occasionally on a brand-new install and it is not your configuration. The JWKS cache is cold: the service verifies assertions against Google's published keys, and a cold cache fails closed rather than accepting an unverified assertion. Reload. It clears. We would rather ship a first-minute 401 you have now been warned about than a verifier that fails open under exactly the condition an attacker would try to induce.

  5. 5

    Ask for something real

    Ask a strain in the console chat for a small service. It writes the source, builds the container, deploys it to Cloud Run in your project, then fetches the resulting URL anonymously and reports the HTTP code back. Then open the journal and read what it did — who staged the job, the exact command, what it returned.

Leaving

Uninstall, and what "portable" means here

An install you cannot cleanly remove is a hostage situation. This one comes out.

cd paracoding-oss
bash uninstall.sh     # from the release archive:  ./uninstall.sh

uninstall.sh removes both Cloud Run services — the console surface and the MCP surface. Project-level resources you may want to keep or inspect (the buckets, Firestore, secrets, KMS key material) are yours; a key you have destroyed is not recoverable, so removing state is left as a deliberate act rather than a side effect of running an uninstaller. If you want everything gone, delete the project: that is the cleanest possible removal and it is available precisely because we never held anything.

Portability is the other half of the same property. Memory, journal, history, files and git all live in your project, encrypted. Hit a usage limit, move to another Claude plan, paste the bootstrap, and the agent resumes with its full history intact. The state was never in the chat window, so there is nothing to lose when the chat window goes away.

Nine codified lockout classes. The installer and the running system block the changes that destroy your own way back in — the ones that leave you locked out of a project you own. That is a class of mistake worth spending code on, because it is the one you cannot fix from inside.

Common questions

Before you open an issue

The four that come up most.

./install.sh: Permission denied

You are on a clone. Run bash install.sh, or chmod +x install.sh first. Files committed through GitHub's web UI carry no executable bit; the release tarball preserves 0755.

Step 0 stopped and named a permission I do not have.

That is the installer working. Grant it (or run as an identity that has it) and run again. The installer is written to be re-runnable; it would rather stop early than leave you with a half-configured project.

My account cannot be added to the allowed list.

Almost always constraints/iam.allowedPolicyMemberDomains refusing an out-of-domain member at binding write. Use an account in the project's domain, or change the org policy knowingly. The refusal is the control doing its job.

Which release should I install?

v10.0. It is the current release. Older tags are not supported and older links are dead. Watch the repo or subscribe to the releases feed — see get notified.