Repository source for agents

Repository source is the committed Git tree Tokay builds and deploys. It is separate from Service files, which are runtime data from a deployed container or Saved storage.

Last updated

REPO_READ grants the complete committed tree, including binaries, generated files, dotenv files, and any secrets committed by the user. Git is the byte store. GraphQL exposes immutable metadata, and HTTP streams bytes.

For git transport access, read the remote URLs from Repo.gitSshUrl and Repo.gitHttpUrl. Never construct git server URLs from ids or hostnames yourself. gitHttpUrl carries no credentials. HTTP Basic auth is x-access-token:<token>.

Discover the current snapshot

query RepoSource($repoId: ID!, $after: String) {
  repo(id: $repoId) {
    id
    repoType
    viewerCanReadSource
    currentSourceSnapshot {
      id
      commitHash
      treeOid
      sourceEntryCount
      sourceTotalBytes
      sourceWarnings { code message }
      entries(prefix: "", depth: 1, first: 1000, after: $after) {
        nodes { path parentPath name kind mode oid sizeBytes }
        pageInfo { hasNextPage endCursor }
      }
    }
  }
}

snapshot.id is a Relay node ID. Keep it and commitHash with your analysis. Page until hasNextPage is false, passing the returned endCursor unchanged. A cursor is bound to its snapshot, prefix, and depth and cannot be reused for another query.

prefix: "", depth: 1 lists the root. To explore a directory, pass its exact path as prefix. Increase depth for bounded recursive exploration. Directories are derived metadata rows. REGULAR and SYMLINK entries have byte bodies, and a symlink body is its target text. GITLINK identifies a submodule commit and has no file body.

sourceWarnings may contain GIT_LFS_OBJECTS_UNAVAILABLE. Tokay exposes committed LFS pointer blobs, not the LFS objects the provider hosts. The repository remains usable, but builds receive those pointer bytes.

Fetch bytes

Send the same bearer token used for GraphQL:

GET /repo_source_snapshots/<snapshot.id>/content?path=src%2Fmain.py
Authorization: Bearer <token>

The response is an attachment with sniffing disabled. It streams the exact indexed Git blob and supports text, binaries, and blobs up to 100 MiB. A gitlink returns SOURCE_CONTENT_UNAVAILABLE.

Fetch a whole repository or directory in one request:

GET /repo_source_snapshots/<snapshot.id>/archive?path=src&format=tar.gz
Authorization: Bearer <token>

path may be empty for the root, and format is tar.gz or zip. Tar preserves executable bits and symlinks most faithfully. ZIP is convenient for browsers, but extraction tools vary in POSIX and symlink fidelity. Archives preserve committed names, so extract untrusted repositories into an isolated workspace. A name or symlink target that is legal in Git but cannot be represented safely fails the whole request with a typed ARCHIVE_* error before headers instead of being renamed or omitted.

Normal fast-forward pushes usually leave old objects reachable. A force push followed by Git garbage collection can expire a snapshot. On SNAPSHOT_EXPIRED, resolve currentSourceSnapshot again. Do not retry the old snapshot.

Download in a browser

Mint a download ticket while authenticated, then navigate to the returned path:

POST /repo_source_snapshots/<snapshot.id>/download_ticket
Authorization: Bearer <token>
Content-Type: application/json

{ "path": "assets", "format": "zip" }

Formats are raw, tar.gz, and zip, and raw requires a file path. The returned downloadPath needs no bearer token because the opaque ticket is the authorization boundary. The ticket lasts five minutes, works once, and is burned when download begins.

Write a managed repository

Writes stay sparse and guarded by an expected commit. Small UTF-8 text can be sent inline. Upload binary or large content first as a raw stream:

POST /repos/<repo.id>/source_blobs
Authorization: Bearer <token>
Content-Type: application/octet-stream
Content-Length: <bytes>

<raw bytes>

The response contains an opaque stagedBlobId, SHA-256, size, and expiry. A staged blob is bound to the principal and SYSTEM repo, expires after one hour, and remains retryable after a push conflict.

Commit text and staged bytes together:

POST /push_bundle
Authorization: Bearer <token>
Content-Type: application/json

{
  "repoId": "<repo.id>",
  "expectedCommitHash": "<snapshot.commitHash>",
  "upsertFiles": [
    { "path": "src/main.js", "content": "console.log('hi')\n" },
    { "path": "assets/logo.png", "stagedBlobId": "<opaque handle>" }
  ],
  "deletePaths": ["old.txt"]
}

Every upsert has exactly one of content or stagedBlobId. Deletes are exact file paths. Omitted paths are preserved. For the first commit to a new managed repo, send expectedCommitHash: null.

Ordinary API /push_bundle has no whole tree replacement mode. The official Tokay MCP adds one explicit COMPLETE_ARCHIVE mode for a complete repository upload. On an existing SYSTEM repo, push_code deletes paths the archive omits only when the upload was declared complete and the caller supplies the exact current commit. Never infer replacement from an ordinary archive or sparse API push.

On GIT_PUSH_CONFLICT, refresh the current snapshot, reconcile your operations, and retry. A staged blob used by the failed attempt remains available. /push_bundle and staged uploads are only for SYSTEM repos. Edit a USER repo with the user's Git workflow and an EXTERNAL repo at its upstream provider.

Limits and stop conditions

  • Blob, raw read, and staged blob: 100 MiB each.
  • Committed tree: 50,000 entries, 5 GiB declared bytes, and 16 MiB canonical metadata.
  • Sparse change: 10,000 paths and 1 GiB. Inline text is 1 MiB per file and 50 MiB total.
  • Archive: 50,000 entries and 1 GiB declared content.

A failure fails the whole operation. Tokay never silently omits paths. Treat authorization and unsupported path errors as terminal until input or grants change. Retry GIT_PUSH_CONFLICT only after refreshing. Refresh on SNAPSHOT_EXPIRED. Respect 429 concurrency responses before retrying.

If viewerCanReadSource is false, stop and report missing REPO_READ. If it is true but currentSourceSnapshot is null, the repository has no successfully ingested commit yet.