agentmesh · your own cloud

Your agent, in your Google Cloud project

You grant AgentMesh permission to act inside a project you own. We deploy your agent there and operate it. No key crosses, every token we hold expires in minutes, and you revoke us by removing one IAM binding without telling us first. Your bill, your data, your audit log.

the mechanism

A role, never a key

You create one service account in your own project and grant our deployer service account permission to impersonate it. Every call we then make in your project is made under a token minted for that moment and good for minutes.

Our deployer service account is:

agentmesh-deployer@langbench-1528148150979.iam.gserviceaccount.com

That is the only identity of ours you ever name in a binding. It holds no role in our own project, so it can read nothing of ours either. It exists to be the principal your grant points at, and to be impersonated onward.

The chain is two hops rather than one, and the reason is worth knowing. A Google Cloud machine's own identity is the project's default Compute Engine service account, shared by everything running on it. We refuse a grant on your Compute Engine default account for exactly that reason, so we cannot offer ours in good faith either. Our machine becomes the deployer account first, and the deployer account becomes yours:

the machine's identity   ->   agentmesh-deployer   ->   your service account
(our default compute SA)      (ours, no roles)        (yours, roles you wrote)
what this is not

Not a service account key. A key is a long-lived secret, it is the thing Google's own guidance says to stop issuing, and refusing one even when it is offered is the point of this route. Not Workload Identity Federation. Federation is for workloads outside Google Cloud that need an identity inside it. Our deployer is already a Google Cloud workload, so impersonation is the direct path.

what you create

You run every command, including the ones that touch your bill

Setup is five steps inside your own project: enable the services we will use, create the service account we act as, create the Artifact Registry repository and the storage bucket the agent works in, grant that service account the roles on the published list, and let us impersonate it. You create the repository and the bucket, so we never need permission to create anything at that level, and you enable the services, because enabling a service can change what your project is billed for.

The exact roles, one at a time, each with its reason and its command, are published in full at https://agentmesh.ai/your-own-cloud.html. Read it before you grant anything. It is the complete list rather than a summary of one, and it names the narrower role wherever a predefined one would have been too strong. It is also the page to hand a security reviewer: the four claims under Our safeguards are the ones they will want first, and each one is something your own IAM policy will confirm rather than a promise about our conduct.

Only one binding on that page names us, and it is the last step:

gcloud iam service-accounts add-iam-policy-binding THEIR_SA \
  --member="serviceAccount:agentmesh-deployer@langbench-1528148150979.iam.gserviceaccount.com" \
  --role="roles/iam.serviceAccountTokenCreator" \
  --project=YOUR_PROJECT

THEIR_SA is the service account you made for this, ending in @YOUR_PROJECT.iam.gserviceaccount.com. It has to be an account you created: we refuse a grant on a Google-managed account such as the Compute Engine default, because a grant on that account is a grant over everything else already running in your project.

recording the grant

We use it before we believe it

Recording a grant and checking it are one call. A door that only recorded would leave you holding a row that proves nothing, so the answer to "I have granted it" is either "we just used it" or the sentence explaining what we hit.

POST /v1/accounts/:id/cloud/grants

{ "provider": "gcp",
  "project_id": "your-project",
  "service_account": "agentmesh-agent@your-project.iam.gserviceaccount.com" }

Three members are read and no others. state, verified_at and last_error are deliberately not among them: a record is not a place where the subject writes the verdict. The service account must belong to the project it names, and a pairing that disagrees is refused at the door, where it is a typo you fix in five seconds, rather than at deploy time, where it is a failure with our name on it.

The answer carries the grant as we stored it, plus the two things you cannot work out for yourself:

{
  "grant": {
    "account_id": "...", "provider": "gcp",
    "project_id": "your-project",
    "service_account": "agentmesh-agent@your-project.iam.gserviceaccount.com",
    "granted_at": "...", "verified_at": "...", "state": "verified",
    "last_error": null, "deploys_here": true
  },
  "grant_to": "agentmesh-deployer@langbench-1528148150979.iam.gserviceaccount.com",
  "permissions": "https://agentmesh.ai/your-own-cloud.html"
}

What the check actually does. One generateAccessToken through the same two-hop chain the deployment manager uses, then one cloudresourcemanager.projects.get on the project the grant names. That read returns a name and a number and nothing anybody would mind us seeing, and every role on the permission list already carries it, so it asks nothing extra of you. It also cross-checks the pairing: the project it answers for has to be the project in the record.

why the check mints its token the way the deploy will

A check that minted some other way could report a working grant on a chain the deployment manager never uses. The way to predict a call is to make it.

the doors

Six routes, and what reaches each one

RouteWhat it doesReachable with
POST /v1/accounts/:id/cloud/grantsRecord a grant and verify it in the same call.A signed-in session
GET /v1/accounts/:id/cloud/grantsThe account's clouds, each with deploys_here.A signed-in session
GET /v1/accounts/:id/cloud/grants/:providerOne grant, whole.A signed-in session
POST /v1/accounts/:id/cloud/grants/:provider/verifyCheck it again, now. Takes no body at all.A signed-in session
DELETE /v1/accounts/:id/cloud/grants/:providerForget our record. Changes nothing in your project.A signed-in session
GET /v1/accounts/:id/cloud/actionsEvery call we made in your project.A session, or an amt_ token carrying audit

The grants family takes no API token, and that is a decision rather than an omission. Creating a grant names the project every future deploy for the account is aimed at, which is the exact input the misdirection guard exists to keep out of a request, and deleting one silently re-points that work at our machines. The re-verify would have been safe on its own, and it is not worth a door the other three could ride through. The action log is a read, so it is open under audit: a list that exists to be offered rather than requested should not need a browser.

The re-verify takes no body for the same family of reasons. A re-check that accepted a project id would be a door through which a request could aim a verification at a project nobody granted.

the four states

One state deploys. The other three withhold

StateWhat it meansDeploys there
pendingRecorded, not yet proven. Nothing has been deployed into the project.No, withheld
verifiedWe impersonated the account and completed the harmless read.Yes
revokedImpersonation was refused: the binding or the service account is gone. Your right, and no incident.No, and we stop retrying
unreachableThe check could not be completed. An outage, possibly ours.No, and we keep retrying

A grant that is not verified withholds the work. It never falls back to our machines. That is the trap this design is built around. An absent grant means our project, so reading "no instruction" as "deploy normally" would quietly put your agent on our machines the morning your grant went unreachable, which is the opposite of everything this route promises. The answer says which state it is in and why, and deleting the record is the deliberate act that means "yes, run it on your machines".

Only a refusal at the impersonation step counts as a revocation: 403, meaning the binding is gone, and 404, meaning the service account is gone. Both are deliberate acts of yours. A refusal at the read step is a statement about the roles rather than about the grant, and everything else that goes wrong is unreachable, the state that means try again. When a refusal is ambiguous we ask one further question, about our own hop alone, and only when that one succeeds do we read the refusal as yours. Recording our own misconfiguration as your revocation would be a lie that never heals, because a revocation is not retried.

A real deploy can lower a state and can never raise one. The deployment manager observes your grant every time it makes a call, and a call refused inside your project is better evidence of a revocation than any check of ours. It is not evidence of success, so it promotes nothing: verified keeps meaning "we impersonated, and the read completed".

revoking

One binding, and no conversation with us

gcloud iam service-accounts remove-iam-policy-binding THEIR_SA \
  --member="serviceAccount:agentmesh-deployer@langbench-1528148150979.iam.gserviceaccount.com" \
  --role="roles/iam.serviceAccountTokenCreator" \
  --project=YOUR_PROJECT

You do not need to tell us and you do not need our agreement. Within minutes every call we try to make fails, because minutes is all the life a minted token has. We record it as a revocation rather than as an error, we stop retrying, and the grant says so. We will not keep knocking.

Deleting our record is a different act, and the answer says so every time. DELETE removes our pointer at your cloud. It does not touch the IAM binding, which is yours and only yours to remove. What stays behind is whatever we already created: the Cloud Run job, the secrets, the bucket contents and the image. They are in your project, they are yours, and they are yours to delete. Ask us to remove the deployment before you revoke, and we tear down only what we created and tell you what is left.

checking us

Two lists that should agree, and yours is the one to believe

Everything we do in your project is done as the service account you created, so your own audit log holds the complete record without us providing anything:

gcloud logging read \
  'protoPayload.authenticationInfo.principalEmail="THEIR_SA"' \
  --project=YOUR_PROJECT --limit=100

We publish the same list from our side, without being asked:

GET /v1/accounts/:id/cloud/actions   # ?provider=gcp&limit=500

Each line is the deployment manager's own report of one call: when it happened, which command ran, how long it took, and how it ended. The project and the service account on every line are read from the grant rather than from the filing, so the log cannot be made to say we acted somewhere we were never granted. We cannot read your Cloud Audit Logs at all, which is what makes the second list worth checking the first against. If the two ever disagree, yours is the one to believe, and we would want to know.

plainly stated

What this costs you

Once your agent runs in your project, its availability is your project's availability: your quota, your regions, your outages. We operate it, and we cannot speak for any of that. Our status page does not cover your project and will not pretend to.

Your model API key goes into your Secret Manager, placed there by us under the impersonated account and read by the Cloud Run job when it starts. We never read the value back into anything of ours. The honest version of that sentence, including the one place where the identity we borrow can do more than our code does, is on https://agentmesh.ai/your-own-cloud.html under the runtime accessor role, along with the two-account arrangement that closes it.

Google Cloud is the cloud this is built for. The grant door refuses any other provider by name rather than accepting it and failing later. If you are somewhere else today, the route that already works anywhere is your own process with our SDK beside it: see bring your own agent.