Pando API
Shape:
- A monorepo is
owner/name, like GitHub: the owner is a namespace (a person's login, or an organization's), the name is unique within its owner, and a permanentidkeys everything stored. A rename or a transfer changes the label only; the oldowner/namekeeps working (an alias) until its owner gives that name to another monorepo, which the create or rename answer reports (replaced_alias). Owners and names match without case and are stored lower case. - In payloads,
monorepois always theowner/namelabel andmonorepo_idalways the id (repos, hook payloads, feed lines, needs-you rows, socket frames). The Monorepo object itself hasid,owner,nameandfull_name. - Every route is under
/v1/monorepos/{owner}/{monorepo}, except the user's own (/v1/user,/v1/monorepos) and the web home's, which span all of the person's monorepos (/v1/events,/v1/branches). A monorepo holds repos. A plain git repo is a monorepo of kindrepoholding exactly one repo, at path., and no meta layer; it uses the same routes. The meta layer's routes (/v1/monorepos/{owner}/{monorepo}/meta/...) are not in this reference. - A repo has a
path(its folder from the root:projects/web, or.in a plain repo) and aname, its id in URLs: the path with each/turned into-(projects-web). A plain repo's name is the monorepo's name when it was made (a rename does not change it). Names are unique in a monorepo. - In a monorepo, the repo named
meta(path., clone_url<host>/<owner>/<monorepo>/meta.git) is the meta layer's own git history. It takes branches, diffs, reviews and merges like any repo, and it is how.pando/**changes: after a merge into it, the merged files go to the live meta layer on every machine. It is not in listRepos. - Git goes through the git door at the repo's
clone_url:https://<host>/<owner>/<monorepo>/<name>.gitfor a monorepo's repos,https://<host>/<owner>/<name>.gitfor a plain repo (smart HTTP; see thegittag). Pushes to the default branch are refused, except the first push that creates it. Every other push goes through. - Every other call is JSON with
Authorization: Bearer <credential>.
Artifacts repos, in the namespace pando-dev, are named from the
monorepo's id and kept on each repo's record (Artifacts has no rename):
a project repo is <id>--<name> (m_0123456789abcdef--projects-web), a
plain repo <id>--<name>, and the meta layer <id>--meta. New ids are
m_ and 16 hex characters; a monorepo made before namespaces kept its
old name as its id. Ids and names have no --, so the split is never
ambiguous. These names are internal: clients use clone_url.
Required checks: the server reads required_checks from
.pando/pando.toml on main. In a monorepo
that is the meta layer's main, table [repos."<repo path>"]; in a plain
repo, the repo's own main, table [repos."."]. No main or no file means
no required checks. When Artifacts fails to read it, or it does not
parse, they are unknown, and only the merge fails closed: reads (the
repo, the branch list, a branch, a commit's status, CI) answer as usual
with the list null and checks_unknown saying why (for a bad file,
"pando.toml line N: "), checks state is unknown; an
unforced merge is refused with 503 checks_unknown and that reason, a
forced one goes through as forced. Nothing is kept, so the next request
reads it again.
Identity works like GitHub's. A person has a user account and belongs to
monorepos as owner or member. A machine signs in once
(pando login) and holds a credential for that user; git and every agent
on the machine act as that user, the way gh and git credentials do. A
cloud sandbox gets a credential its user makes for it. Credentials are
listed and revocable. Commits are authored as the user; Pando's commit
trailers name the agent.
Any member may push, review, post statuses, report worktrees and merge
(agents included, through the user's credential). Reviews are comments
and approvals, not a gate. The merge gate is the checks, and ?force=1
merges past them. Owners also manage members and hooks.
Organizations: anyone signed in creates one (POST /v1/orgs); its name
is a namespace in the same space as logins. Roles are owner and
member. Everyone in an org can open and push to every monorepo the
org owns, and its owners are owners of all of them: a person's role in
an org's monorepo is the higher of their own row in the monorepo and
their org role. A monorepo can still add people from outside the org
itself. People are added at once (by login, or by an email that has
signed in), or at that email's first sign-in. Removal from an org takes
effect within 15 s, as for a monorepo.
Signing in (pando login): WorkOS AuthKit's CLI Auth, the OAuth device
flow, run through the Worker. POST /v1/login/device returns a code
and a URL; the person confirms the code in the browser; the CLI polls
POST /v1/login/token with the device code until it returns the user and
a new machine credential. WorkOS only proves who the person is: Pando
keeps its own users, credentials and membership, and keeps no WorkOS
token. A user's login comes from their email (ana@x.com is ana, or
ana-2 if taken). Invites by email wait until that email first signs in.
In a browser: GET /v1/login/browser?return_to=<path> sends the person to
AuthKit's page; WorkOS sends them back to /v1/login/callback, which sets
the session cookie __Host-pando_session (HttpOnly, Secure,
SameSite=Lax, host-only) and returns them to return_to (a path on this
host, else /). The session is a credential of kind browser for the
same Pando user, so it sees the same monorepos. It works on /v1 only:
never as a Bearer token and never at the git door. With the cookie, a
write (any method but GET and HEAD) or a WebSocket upgrade must carry
Origin equal to Pando's own origin, else 403 bad_origin. A session
ends after 30 days without use, on sign-out (POST /v1/logout, which also
ends the WorkOS session), or when revoked.
Self-hosted, one person: the Worker secret PANDO_BOOTSTRAP makes users
with POST /v1/users (it works there only). Either way, a user then makes
more credentials (POST /v1/user/credentials) and monorepos
(POST /v1/monorepos).
Agents: an agent acts as its user, through the user's credential. It may
also send X-Pando-Agent: <name> on API calls and pushes; Pando then
records the agent on merges (merged_agent) and on events.
Branch names in paths: encode / as %2F (agent-3%2Foauth). Raw
slashes are accepted too.
Errors are JSON: {"error": {"code": "...", "message": "..."}}.