Start growing
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
Three things, and Cloud Shell already gives you the fourth.
/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.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.Two routes
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.
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
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
./install.sh in one and bash install.sh in the otherFiles 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.
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
Read this before you run it, then read the output as it goes past. The installer narrates.
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.
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.
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.
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.
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.
The console URL, ending in /harness, and the MCP URL, ending in /mcp.
Copy both. They are the whole hand-off.
--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
The most common first-day mistake is pasting one into the other's client. They are different services with different auth models.
| URL | For | Auth | What 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
Console first, then the MCP endpoint. Ten minutes, most of it browser consent screens.
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.
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.
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.
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.
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
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.
Common questions
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.