Clone your circle + run Claude Code
Your circle isn’t just a UI — it’s a real git repository. Anyone comfortable in their own editor and their own coding agent can skip the CLI entirely, clone the circle straight to disk, and point Claude Code (or any other agent) at it directly.
Why and when
Use this lane instead of the Triton CLI when you want:
- Multi-file, IDE-native work — refactors across several elements at once, full-repo search, your own editor’s diagnostics and diffing.
- Your own agent, your own account — no MCP bridge, no extra install; just Claude Code (or Codex, or any agent) working a normal git checkout.
- Familiar git workflow — branch, commit, diff,
git log— the same muscle memory you already have for every other repo.
The two lanes aren’t exclusive. Many people mint a CLI session for quick chat and tool calls, then drop into a full clone for anything that touches several files at once.
Clone
The fastest way to a ready-to-paste clone command is the portal: open the
circle, open the Source Control popover from the breadcrumb, and under
Clone this circle click Mint clone command. That mints a scoped,
expiring token and hands back a complete git clone command with the token
already embedded — copy it and run it:
git clone --recurse-submodules --jobs 8 --branch draft \
"https://<circle>:<token>@triform.cloud/api/<circle>/git" <circle>
cd <circle> && git submodule update --remote --jobs 8
The API equivalent is one call, if you’d rather stay in a terminal:
curl -X POST https://triform.cloud/api/<circle>/ops/source/clone-token \
-H "Authorization: Bearer $ADMIN_JWT" -H 'Content-Type: application/json' \
-d '{}'
# → {"clone_command": "git clone ...<token>...", "token": "trif_...", "expires_at": "..."}
{} defaults to a 7-day expiry and a read-only token — enough to clone
and pull, not enough to push. Pass {"allow_push": true} for a token that
can also push (see Scopes are enforced below — push
requires write/execute, and this is the boolean that grants it). The
token is shown once, in that same response — save it before it scrolls off.
Calling the op again rotates it: the previous token stops working
immediately, so don’t re-mint casually if something else still depends on
the old one.
(Your workspace’s construction notice in the portal shows a similar ready-made clone command with the circle name already filled in, but no credential embedded — the Source Control popover’s mint button above is the one that hands you a complete, working command.)
For CI/automation, or anywhere you want a durable, independently-revocable credential instead of an ad hoc clone-token mint, see Minting a token below.
--branch draft checks out the working branch (see Ship below for
what draft means). Every circle is served as a recursive git repo — the
circle-root is the parent repo, and each element inside it is a submodule, so
--recurse-submodules pulls all of them in one shot.
Notice the URL’s username is the circle slug, not a placeholder — that’s load-bearing, not cosmetic. See Per-circle credentials below for why.
After cloning, pull each element’s latest draft explicitly — the gitlink pins
recorded in the circle-root commit can lag behind an element’s live draft:
git submodule update --remote --jobs 8
--jobs 8 is safe everywhere now, for the initial clone and every submodule
update after it — parallel submodule fetches used to fail unpredictably
because the server didn’t decompress gzipped git POSTs; that’s fixed, so
there’s no reason to fall back to --jobs 1.
Once you’re in the directory, start Claude Code (or your agent of choice) as you normally would:
claude
Auth
triform.cloud has no dev-auth bypass, and your personal login session does
not work for git. The git permission gate authorizes on a token’s own
circle_id/scope_circle_id, not on circle membership — so cloning with your
browser session’s credentials (or a personal API key) fails with a 403, by
design, not a bug. (A credential that doesn’t resolve to any circle at all
fails with a 401 carrying a WWW-Authenticate challenge and a _suggestion
instead; see Troubleshooting.)
This section covers minting a working credential, the scope model behind it, and why the URL’s username matters.
Per-circle credentials, not per-host
Every clone URL on this page embeds the circle slug as the URL username:
https://<circle>:<token>@triform.cloud/api/<circle>/git. Git’s own
credential caches (macOS Keychain, git-credential-cache,
git-credential-store) key their cache entry on (host, username) — so
embedding the circle slug as the username gives every circle’s token its own
cache slot.
The old convention (documented here previously) used a single shared
x@triform.cloud username for every circle, stored via one global
credential.helper store file. That collapses every circle’s token into one
cache slot — the last one written wins, so a perfectly valid token for
circle B silently overwrites circle A’s cached credential, and your next
git command against circle A 403s for no obvious reason. That recipe is
deprecated — use the per-circle-username URL instead, and let your normal
OS credential cache do its job per circle. See
Troubleshooting if you’re hitting this right now.
Minting a token
The Clone section above covers the fastest path: the circle root’s
source/clone-token op, one click in the portal or one curl call, gives you
a working credential embedded in a ready-to-run command. That’s the right
choice for a one-off clone.
For CI/automation, or anywhere you want a durable, independently-labeled and
independently-revocable credential instead of an ad hoc clone-token mint,
create your own api-token element instead:
# create the api-token element once
curl -X POST https://triform.cloud/api/<circle>/ \
-H "Authorization: Bearer $ADMIN_JWT" -H 'Content-Type: application/json' \
-d '{"element_type":"api-token","slug":"git-deploy","name":"Git/CI deploy token"}'
# generate a token (the trif_... value is shown once — save it)
curl -X POST https://triform.cloud/api/<circle>/git-deploy/ops/generate \
-H "Authorization: Bearer $ADMIN_JWT" -H 'Content-Type: application/json' \
-d '{"scopes":["read","write"],"token_type":"ci"}'
Use it as the URL password, with the circle slug as the username:
export TRIF=trif_... # the token from above
git clone --recurse-submodules --jobs 8 --branch draft \
"https://<circle>:$TRIF@triform.cloud/api/<circle>/git" <circle>
Scopes are enforced
A token’s scopes gate what it can do on git, same as on the REST API:
read clones and pulls (any of read/write/execute satisfies it);
pushing needs write or execute. A read-only token clones fine and then
gets a 403 on git push, by design; mint or re-mint with
"scopes":["read","write"] if you need to push. A 403 on git names the
presented token and the scope it’s missing, so you don’t have to guess — see
Troubleshooting for a live example.
See Authentication & access for the full token/scope model.
Ship
Pushing is not the same as going live — three distinct steps, each its own gesture:
- Source. Edit,
git commit, thengit push origin HEAD:draft. This advances the element’s draft ref and reconciles it into the database. It does not build or release anything. - Build (SPA elements only):
POST /api/<circle>/<element>/ops/build. Code elements (Python, Rust functions, etc.) don’t need a build step — the executor runs the promoted branch directly. - Release:
POST /api/<circle>/<element>/ops/source/promote {"target_branch": "demo"}(then again with"live"when you’re ready). This is a git ref move —draft→demo→live— and pushing straight todemoorliveis blocked. For an SPA this is what actually makes the new build visible; for code elements it’s what the executor picks up.
You don’t have to script the three steps by hand — triton push, triton build, and triton promote (installed with the CLI) wrap
them, and work fine alongside a plain git clone.
What’s in the repo
- The circle-root repo holds circle-wide files (a
README.mdthe portal renders in the circle’s Readme tab, and any bundled.claude/skills/). - Each element is a git submodule with its own history, versioned on every change.
- Bundled skills at
.claude/skills/<name>/SKILL.mdin the circle-root travel with the clone and are picked up by Claude Code automatically — no install step.
Troubleshooting
-
401on the git path — no credential reached the server at all (or it resolved to nothing). The response carries aWWW-Authenticate: Basicchallenge plus a_suggestionwalking through exactly this setup: use atrif_token as the Basic password, embed the circle slug as the URL username. If you’re seeing it with credentials embedded in your URL, git likely sent the request before the challenge round-trip — retrying (or just re-running the clone) resolves it, since the server now advertises the challenge. -
403on clone or push, and you’re sure the token is fresh — almost always a stale cached credential, not the new token you just minted. Git found some password for that host before it ever asked you for the new one — often a different circle’s token left over from the old shared-username convention (see Per-circle credentials above). The 403 body names exactly what was actually presented, e.g. for a token scoped to the wrong circle:Read access denied to 'my-circle': authenticated as 'git-deploy', which has no access to this circle (token scope circle: 7f3d…). If you recently rotated tokens, a stale cached credential (e.g. macOS keychain) may be shadowing the one you intended — clear it or embed the circle name as the URL username. Mint tokens via an api-token element in 'my-circle'.or for a push with a read-only token:
Push denied to 'my-circle': API token 'git-deploy' has scopes ["read"] but push requires 'write' or 'execute'If clearing the specific cached credential is inconvenient, force this one command to use exactly the token you intend, bypassing every configured credential helper:
git -c credential.helper= \ -c credential.helper='!f() { echo username=x; echo password=$TRIF; }; f' \ clone --recurse-submodules --jobs 8 --branch draft \ https://triform.cloud/api/<circle>/git <circle>(
$TRIFneeds to be an exported environment variable so the inline helper can see it. The server ignores the URL/Basic username entirely — only the password is checked — soxabove is fine; the fix is bypassing the cache, not the username.) -
Submodules look stale right after cloning — run
git submodule update --remote --jobs 8; the circle-root’s recorded gitlink pins are a snapshot, not always each element’s newest draft. -
Push rejected for a path (
logs/,cache/at root,.env,.log,.db,secrets.*, …) — these are blocked runtime/secret paths, not a bug. Remove the path from your commit (and.gitignoreit). -
CAS repository is corrupt/expected ACK/NAK, got 'PACK'after a rejected or non-fast-forward push — the local clone’s object store is half-written. Don’t try to repair it; re-clone the affected element or circle. -
Pushed but nothing changed live — you completed step 1 (Source) but not steps 2–3 (Build/Release). See Ship above.
Related
- The Triform CLI — the terminal-native alternative to a full clone; mix both against the same circle
- Authentication & access — tokens, sessions, and scopes
- API structure — the underlying HTTP surface