troupe plane · troupe

Troupe plane

troupe

https://troupe.itmindsinternal.dk

Welcome to the troupe, where your actors perform at your whim.

They take direction, they improvise, they will work the night through — and they have the sense to stop and look at you when the script runs out. Nobody takes a bow without your say-so.

Behind the costume: this host runs your team's coding agents on machines it looks after. Pick a door below, or read what any of that means first.

The Troupe mask: one face, the left half black, the right half magenta.
All actors wear masks, which one are you putting on today?

Start in one minute

Step one

Get the binary

One executable, nothing alongside it. It is published separately from this host, so ask your administrator where your organisation keeps it.

Step two

Log in, once

troupe login https://troupe.itmindsinternal.dk

It asks this plane which provider to use and prints a code for you to enter in a browser. A refresh token lands in credentials.json beside your config.yaml (~/.config/troupe, or %APPDATA%\troupe on Windows); that is the only thing stored, and the plane never sees your password.

Step three

Give it something to do

troupe --remote run "fix the flaky test"

troupe --remote opens the full terminal UI, and troupe --remote sessions lists what your team has running.

--plane https://troupe.itmindsinternal.dk picks this plane when you are logged in to more than one, and --agent NAME picks a profile when you are granted several. troupe admin is the console above, from a terminal.

Where that actually runs

Your client signs in to the plane, the plane starts a session on a worker pod, and the transcript streams from that pod straight back to you without passing through the plane. your transcript — straight from the pod to you YOU You troupe --remote the app, in a browser THIS HOST The plane who you are, your teams sessions and tokens never your transcript A WORKER POD A worker pod your session runs here Sealed storage only pods hold the keys sign in · where do I run? run it here
This host is a switchboard, not a middleman. It knows who you are and which pod you may use, and hands your client an address and a short-lived token. After that your client talks to the pod directly — what the session says and does never passes through here. The rest of the picture.

Build against it

1 Your own harness

Everything a client does is JSON-RPC over POST /rpc, and our own clients use nothing else — there is no second path into this plane. Start at the discovery document:

curl -s https://troupe.itmindsinternal.dk/.well-known/troupe

It names the identity provider, the client id, and the device-authorization and token endpoints. Run the device grant against the provider, then trade what it gives you for a plane token:

curl -s https://troupe.itmindsinternal.dk/auth/exchange \
  -H 'content-type: application/json' \
  -d '{"id_token": "<the id token from the provider>"}'

The answer carries token, who you are, and the teams and profiles you may use. A plane token lasts fifteen minutes, so mint a new one from the refresh token rather than holding this one. A service principal posts its client_id and client_secret to the same endpoint and gets the same thing back.

Every call after that looks like this:

curl -s https://troupe.itmindsinternal.dk/rpc \
  -H "authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "me"}'

Methods: me, teams.list, profiles.list, sessions.list, session.create, session.get, session.open, token.mint and the rest. session.open is what hands you a pod's endpoint and a token for it — the live stream is that pod's, not this one's.

2 A model, or another agent

The administrative surface is also an MCP server at /mcp: the same methods as the console, the same token, the same permissions. From a machine that has the binary, a stdio bridge:

claude mcp add troupe -- troupe mcp --plane https://troupe.itmindsinternal.dk

A remote MCP client authenticates to the identity provider itself instead. Point it at https://troupe.itmindsinternal.dk/mcp and it finds the provider through /.well-known/oauth-protected-resource; Troupe is a resource server and never an authorization server.

None of this reads session content. No method returns it — a property of the platform rather than a permission somebody withheld.

A GUI served from any other origin is a cross-origin caller, and this plane answers one only from an origin on its allowlist. When the app cannot reach the plane but the console can, that allowlist (TROUPE_CORS_ORIGINS) is the first thing to check.

Endpoints

GET /docs what this is, for a person who was handed the URL
GET /.well-known/troupe where to log in, and what this plane calls itself
GET /.well-known/jwks.json the keys a worker verifies session tokens against
GET /.well-known/oauth-protected-resource which provider an MCP client authenticates to
POST /auth/exchange a provider token in, a plane token out
POST /rpc the client API, and the admin API, as JSON-RPC
POST /mcp the admin API again, as MCP tools
GET /healthz liveness, for Kubernetes