Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

349 lines · 40.0 KBRaw History

title: Limits description: Every hard limit in Diffinery and where it applies: sign-in, tokens, API, search, archives, releases, git, Actions, sandboxes, agent tasks, agent review, packages, imports, email.

This page lists the fixed limits built into Diffinery: sizes, counts, timeouts and lifetimes. Use it when a push, a workflow, an upload or an API call fails with a size or count error, or when you plan something large. Values come from the code of the running version. Where a limit comes from Cloudflare rather than from Diffinery, the table says so.

Units: 1 KiB is 1,024 bytes, 1 MiB is 1,048,576 bytes, and 1 GB in the cache and artifact rows is 1,073,741,824 bytes. Sizes Diffinery checks itself and writes as "MB" (pushes, release assets, archives, profile pictures, forms) are in MiB: "100 MB" for pushes means 104,857,600 bytes.

Sign-in and sessions

LimitValueApplies to
Browser session lifetime30 days, extended to 30 days from now on each day the session is used, never past 90 days after sign-inThe session cookie you get after signing in with a passkey, a sign-in link or email recovery. An unused session ends 30 days after its last extension, and every session ends 90 days after its sign-in.
Recent sign-in for sensitive actions2 hoursAdding or removing a passkey, creating a token, an agent or an agent token, changing your username, issuing a sign-in link, deleting, transferring or purging unreachable objects of a repository, and deleting an organization. An older browser sign-in gets Confirm it's you (403 with a confirm URL over the API). See Confirming it's you.
Passkey browser prompt2 minutesTime to complete the browser's passkey dialog when signing in or adding a passkey.
Passkey sign-in attempt5 minutesA sign-in or registration that started more than 5 minutes ago can't be completed; start again.
Sign-in link lifetime24 hours, single useLinks a site administrator sends with Sign-in link in Settings → People and invitations.
Invitation lifetime14 daysInvitations to the site, an organization or a repository.
Email recovery session at Cloudflare Access15 minutesThe Cloudflare Access application that guards /auth/access (set by scripts/bootstrap.sh). It only covers the email check; the Diffinery session that follows lasts 30 days.
Username when joining from an invite39 charactersLetters, digits, - and _. Some names are reserved (for example api, settings, login, admin, docs).
Display name when joining100 charactersLonger names are cut off.
Passkey name100 charactersLonger names are cut off.
Repository, organization and agent names1 to 100 charactersStart with a letter or digit; then letters, digits, ., _ and -. Repository names can't end in .git. Organization and agent names can't contain . and can't be a reserved name.
Note on an access request500 charactersThe note someone without an invite sends to site administrators. Up to 20 site administrators are emailed about each request.

Tokens

LimitValueApplies to
Token lifetime1 to 366 days, default 90Personal access tokens and agent tokens. Values outside the range are clamped.
An agent's first token90 days, write scopeThe token created together with an agent, unless you pass token.expires_in_days or token.scopes.
Expiry reminder email7 days before expirySent once per token, and only for tokens that were valid for more than 8 days.
"Last used" accuracy1 hourA token's last-used time is updated at most once an hour.
Creating tokens through the APIA browser sessionNo token can create a token or an agent: POST /api/v3/diffinery/tokens and the agent endpoints return 403 creating tokens needs a browser session: sign in at https://diffinery.com/settings/tokens for every token. Each name in repositories must be a repository you can see, or the request fails with 422 unknown repository acme/website.
Sandbox token lifetime24 hoursA sandbox's git and API credentials stop working 24 hours after it starts, or earlier when it is closed or its creator loses access.
Token scopesread, write or admin; default ["write"]A new token (personal or agent) without scopes gets write. An empty list or any other name returns 422 scopes must be a non-empty list of: read, write, admin. Repeated names are stored once.
Writes with a read tokenRefusedEvery change returns 403 this token needs the write scope. Git fetch and Git LFS downloads still work.
Account-level actionswrite to create repositories, mark notifications read, change notification settings and send a test email; admin for creating organizations, invitations, organization secrets, agents and revoking tokensA token restricted to some repositories can't do any of these (403 this token is limited to repositories and can't change account settings).

REST API

LimitValueApplies to
Rate limitsPer minute: 20 sign-in attempts and 300 other requests per IP address without signing in; 2,000 requests and 60 new repositories, organizations, forks, issues, pull requests, releases, comments or reviews per accountPast a limit, 429 with retry-after: 60. Jobs, sandboxes, runners and imports aren't counted. See Rate limits.
Request body size100 MBCloudflare refuses larger request bodies before they reach Diffinery.
Items per page30 by default, 100 at mostEvery paginated list (per_page). Larger values are treated as 100. page can go up to 100,000.
Workflow runs per page25 by default, 100 at mostGET /repos/{owner}/{repo}/actions/runs.
Commits in a comparison or pull request250 in total, 100 per pageGET /repos/{owner}/{repo}/compare/{base}...{head} and GET .../pulls/{n}/commits.
Refs per page100 by defaultGET .../git/refs and .../git/matching-refs/{ref}.
Environment variables per page10 by default, 100 at mostGET .../environments/{env}/variables.
Commits searched for a pathThe latest 2,000 commitsGET /repos/{owner}/{repo}/commits?path=... and the history of a file in the web UI only look at the 2,000 most recent commits.
Package versions per page30 by default, 100 at mostGET .../packages/container/{name}/versions.
Repository lists500 repositoriesGET /user/repos, /users/{login}/repos, /orgs/{org}/repos page over at most 500 repositories (most recently pushed first).
Notifications100GET /api/v3/notifications returns your 100 most recent (unread unless all=true).
Email log50 by default, 500 at mostGET /api/v3/diffinery/emails?per_page=.
Job log lines per call while a job runs5,000GET /repos/{owner}/{repo}/actions/jobs/{id}/logs. Pass after=<seq> with format=json to read the next lines. A finished job's log comes back whole.
Recursive tree listing100,000 entriesGET .../git/trees/{sha}?recursive=1 stops there and returns truncated: true.
Entries changed by one tree request10,000POST .../git/trees (a tree request can change at most 10000 entries).
File and directory names255 characters per name, 4,096 per pathEvery API write (contents, git trees, multi-file commits). Names ., .., .git (in any case, and spellings such as .git. or git~1) and names with \ or NUL are refused.
Topics20 per repository, 50 characters eachPUT .../topics. Lowercase letters, digits and hyphens, starting with a letter or digit.
LabelsNames up to 100 characters; 100 labels per issueThe REST API. The web UI's label form allows names up to 50 characters.
Assignees10 per issue or pull requestan issue can have at most 10 assignees.
Issue and pull request textTitles up to 256 characters; bodies, comments and review text up to 65,536 charactersAs on GitHub, everywhere they're written (web, REST, MCP). Longer text fails with 422 body is too long (maximum is 65536 characters).
MCP batch20 requestsOne POST /mcp with a JSON-RPC batch of more than 20 requests answers 400 with at most 20 requests in one batch.
repository_dispatchevent_type up to 100 characters; client_payload up to 10 top-level properties and 64 KiBPOST .../dispatches.
Check run name255 charactersPOST .../check-runs.
Deployment required_contexts100 contextsPOST .../deployments.
Environment name1 to 255 charactersNo /, \ or control characters.
LimitValueApplies to
Query length256 charactersq of GET /search/issues and /search/repositories. Longer queries fail with 422 The search is longer than 256 characters.
Results you can page to1,000Asking for a page past the first 1,000 results fails with 422 Only the first 1000 search results are available.
Repositories an issue search covers50The first 50 repositories you can read, out of the up to 500 most recently pushed repositories visible to you. More sets incomplete_results: true; add repo: or org: to narrow it.
Repositories a repository search checks300Candidates checked for access, out of the same up to 500. More sets incomplete_results: true.
Issues scanned per repository5,000The most recent issues and pull requests in each repository.
Comments read for in:comments2,000 per repositoryThe most recent comments.
Issue search box in the web UI200 charactersThe search query on the issues and pull requests pages.

Source archives

LimitValueApplies to
File content per archive24 MBzipball, tarball and /<owner>/<repo>/archive/<ref>.zip or .tar.gz. Larger repositories fail with 413 repository is too large for an on-the-fly archive; clone it with git instead.
Tree entries per archive100,000Every entry visited counts, including repeated subtrees. More fails with 413 the tree has more than 100,000 entries; clone it with git instead.
Directory depth128 levelsDeeper trees fail with 413 the tree is nested too deeply.
Files in a zip65,535More fails with 413; download the .tar.gz archive instead.
New archives per repository20 per hourArchives already built and cached don't count. Past the limit: 429 too many archives were built for this repository in the last hour; try again later or clone it with git.
CacheNot guaranteedA built archive is cached in R2 and can be evicted at any time; it is then built again.

Releases

LimitValueApplies to
Asset size100 MBEach asset uploaded to a release's upload_url, the largest request body Cloudflare Workers accept (413 release assets are limited to 100 MB).
Asset size through MCP25 MBThe upload_release_asset tool (MCP uploads are limited to 25 MB; use the upload_url of the REST API for larger files).
Files per web form submission100 MB in totalThe release form in the web UI. Upload larger files with the REST API.
Asset name1 to 255 charactersAfter unusual characters are replaced with .. Each name once per release.
Release notes125,000 charactersbody of a release.
Releases listed through MCP100list_releases (limit, default 30).

Commit statuses and checks

LimitValueApplies to
Statuses per commit and context1,000422 this SHA and context has reached the maximum number of statuses.
Status context255 charactersLonger contexts are cut. Without context, it is default.
Status description1,024 charactersLonger descriptions are cut.

Profile pictures

LimitValueApplies to
Upload size5 MBPictures for people and organizations, in the web UI and through PUT /api/v3/user/avatar or /orgs/{org}/avatar (That picture is larger than 5 MB. Use a smaller file.).
Image size3072×3072 pixelsLarger images are refused.
FormatsJPEG, PNG and WebPOther files fail with Use a JPEG, PNG or WebP image.
Stored sizes64, 128 and 256 pixels squareThe picture is cropped to a square and stored as WebP. GET /api/avatars/{login}?s= serves the smallest stored size at least as large as asked.

Git

LimitValueApplies to
Push size100 MB per pushThe whole push request after decompression. A larger push is rejected with push exceeds 100 MB; split it into smaller pushes. Push in several steps (for example a range of commits at a time).
Fetch request size16 MBThe request a client sends to fetch or clone (its wants and haves), after decompression. A larger one is refused with 413 fetch request exceeds 16 MB.
File size in a pushUp to the push sizeA single file (blob) can be as large as fits in one push (GitHub allows files up to 100 MB). Files of 4 MiB or more are checked and stored from their compressed bytes, so they never need to fit in memory inflated.
Unpacked size of a push512 MiBWhat a push's pack inflates to, deltas resolved. A pack that unpacks to more is refused, however small it is on the wire.
Git protocolVersion 2 for fetch and cloneClients older than git 2.26 must run git config --global protocol.version 2, or the server answers Diffinery requires Git protocol v2.
Secret scanning coverageEvery new text filePush protection scans every new text file in a push, whatever its size. The rejection message lists up to 10 findings.
Linear history check1,000 new commitsA protected branch with "require linear history" checks up to 1,000 new commits in a push for merge commits.

Git LFS

LimitValueApplies to
Objects per batch request1,000Objects beyond the first 1,000 in one LFS batch request are ignored; the git-lfs client batches in smaller groups by default.
Upload and download links1 hourThe expires_in of the transfer links the batch API returns.
Object size100 MBEach object is uploaded in one request, and Cloudflare refuses request bodies over 100 MB.

Pull requests, diffs and the web UI

LimitValueApplies to
Files in a diff300Commit, compare and pull request diffs (web UI, REST and MCP) include the first 300 changed files and mark the diff as truncated.
Size of one file's diff1 MiB (old and new version together)Larger files show no line diff ("This diff is too large to display.").
Content diffed per request16 MiBOne commit, comparison or pull request diffs at most 16 MiB of file content; later files are listed without a line diff.
Changed lines diffed exactly1,000 per file, about 2,000 per requestA file with more changed lines, or one diffed after the request's budget is spent, is shown as one block: the lines both versions share at the start and end stay as context, and everything between is removed and added. The line counts stay right; the diff is just not minimal.
Automatic line merges2,000 changed lines per sideMerging a pull request, a file both sides changed by more than this is a conflict to resolve yourself.
Commits listed in a comparison or pull request250GET /repos/{owner}/{repo}/compare/{base}...{head}, GET .../pulls/{n}/commits and the matching web pages.
Viewing or editing a file in the browser1 MiBLarger files show "This file is too large to display" with a View raw link, and can't be edited in the browser. READMEs over 1 MiB are not rendered.
Markdown source512 KiBLonger Markdown (READMEs, issue and pull request bodies, comments) is shown as plain preformatted text.
Markdown nesting12 levelsDeeper nested lists and quotes are shown as plain text.
Markdown inline run60,000 charactersA longer paragraph is shown without inline formatting.
Web list pagesPage 1,000Paginated lists in the web UI stop at page 1,000.
Syntax highlighting2,000 lines and 200,000 characters per fileLarger files are shown without highlighting. Lines over 2,000 characters are not highlighted.
Web form size10 MBForms in the web UI, except the release form (100 MB of files) and profile pictures (5 MB).
Required approvals0 to 10The Required approvals setting of a branch protection rule in the repository settings page.
Assignees per edit10Assigning people from the issue or pull request sidebar.
Label name and description50 and 100 charactersLabels created in the web UI.
Merge queue build concurrency1 to 20, default 5How many merge groups a queue builds and tests at the same time (mergeQueue.buildConcurrency).
Merge queue check timeout1 to 360 minutes, default 60How long a merge group's required checks may take before the pull request leaves the queue (mergeQueue.checkTimeoutMinutes).

Actions

LimitValueApplies to
Job timeout360 minutes (6 hours), default and maximumjobs.<id>.timeout-minutes. Higher values are lowered to 360. A job that runs out of time ends with conclusion timed_out and the message "The job exceeded its timeout of N minutes."
Step timeoutNo maximum beyond the job'ssteps[*].timeout-minutes.
Waiting for a concurrency group6 hoursA run whose concurrency group is busy (without cancel-in-progress) checks once a minute for up to 6 hours.
Jobs in one matrix256More combinations fail the job with "A matrix may produce at most 256 jobs."
Matrix combinations before exclude4,096The product of the matrix's vectors. A larger matrix is refused without being expanded: "the matrix has <n> combinations before exclusions; at most 4096 are allowed (and a matrix may produce at most 256 jobs)".
Matrix include and exclude entries256 each"matrix.include has <n> entries; at most 256 are allowed".
Workflow files read per commit256Of .github/workflows and .diffinery/workflows together, in name order; later files are ignored.
Workflow file size512 KiBA larger file is listed as invalid ("the workflow file is larger than 512 KiB") and never runs.
Reusable workflow nesting4 levels"reusable workflows can be nested at most 4 levels deep"
Reusable workflows per run20 calls"a run can call at most 20 reusable workflows"
workflow_run chains3 levelsA run started by workflow_run that is itself three links down a chain triggers nothing (A starts B starts C starts D; D's runs start nothing).
Schedule granularity5 minutesScheduled workflows are checked every 5 minutes. A cron that fires more often still runs at most once per 5-minute window. Only workflows on the default branch of repositories that aren't archived are scheduled.
Schedule catch-up60 minutesA check that comes late, or fails, is caught up by the next one, up to an hour back. Each schedule starts once per check, however many of its minutes the check covers.
Starting a job3 retriesStarting a job's container is retried 3 times with exponential backoff from 10 seconds before the job fails with "Could not start a runner".
Hosted runner sizeCloudflare Containers standard-2Each runs-on: ubuntu-latest job runs in its own container of this instance type.
Hosted job containers at once100max_instances of the runner container class.
Hosted jobs at once per repository20Further jobs stay queued and start, oldest first, as the repository's own jobs finish, so one repository can't take every container. Self-hosted jobs don't count.
No free containerWait, then retry, 8 timesWhen the platform has no container free for a job (many jobs starting at once, or just after a deploy), the job goes back to queued and starts again after 30 seconds, then 1, 2, 4 and 5 minutes. After 8 tries, about half an hour, it fails with No hosted runner could be started. Only jobs whose runner never started are retried, so no step runs twice.
Unfinished runs per repository500Past it, a workflow_dispatch or repository_dispatch is refused with 429 this repository has 500 unfinished workflow runs, the most allowed; wait for some to finish, and other events start no run.
Self-hosted runners per repository100, each with at most 20 labelsRegistering one more gets 429 this repository has 100 self-hosted runners, the most allowed; remove one first. A registration token works for an hour.
Purging unreachable objectsRepositories of up to 300,000 objectsA larger repository gets 422; ask a site administrator. See Removing a secret that was pushed.
Jobs during a deployNot interruptedA deploy doesn't replace the container of a running hosted job: it finishes on the version it started with. If the platform replaces a job's container anyway, the job keeps going for up to 13 minutes, then is cancelled.
Service container health check15 minutesHow long a job waits for a services: container's health check to pass.
Log line length16,384 charactersLonger lines are cut and end with ….
Log size per job16 MiB of textThe log ends with "The log is cut off here: a job's log is limited to 16 MiB of text." and later lines are dropped; the job keeps running.
Log shown in the web UI20,000 linesThe job page shows the first 20,000 lines and links to the full log.
Job log retention90 daysA finished job's log is stored for 90 days, then deleted. The run and its jobs stay listed.
OIDC token lifetime10 minutesID tokens from permissions: id-token: write.

Caches and artifacts

These apply to actions/cache and actions/upload-artifact (and the matching built-ins) in hosted and self-hosted jobs. See Artifacts and caches in the REST API reference for listing and deleting them.

LimitValueApplies to
Cache storage per repository10 GBEvery cache entry of the repository together, on all branches. When saving a cache would go over 10 GB, the least recently used entries are deleted until it fits.
Unused cachesDeleted after 7 daysAn entry that no job restored for 7 days is deleted the next time a job saves a cache in the repository.
Cache entry size10 GBA larger entry fails with 413 a cache entry may be at most 10737418240 bytes. If caches still being uploaded by other jobs fill the budget, saving fails with 413 uploads in progress use the repository's whole cache budget.
Cache key512 characters, no commasLonger keys or keys with , fail with 400 invalid cache key. Up to 20 restore keys per lookup.
Saving an existing cache keySkippedA key and version that already exist for the branch are not replaced; the save is skipped with a warning.
Which caches a job can restoreIts own branch, then the base branch, then the default branchA pull request job also restores caches of its base branch, a merge queue job those of the target branch.
Artifact storage per repository10 GB by defaultArtifacts that haven't expired plus uploads in progress. Past the quota, an upload fails with 413 the repository's artifact storage quota (N bytes) is used up; delete artifacts or wait for them to expire. Operators change it with ARTIFACT_QUOTA_BYTES (see Configuration).
Artifact retention1 to 90 days, default 90retention-days. Values outside the range are clamped.
Expired artifactsListed for 90 more daysAfter its retention the content is deleted. The artifact stays in the API with expired: true, and downloading it returns 410 Artifact has expired.
Artifact name256 characters, unique per runNames can't contain ", :, <, >, |, *, ?, \, / or line breaks. A second artifact with the same name in a run fails with 409 an artifact with this name already exists on the workflow run.
Upload request size256 MiB, and Cloudflare's 100 MB request limitactions/cache and actions/upload-artifact upload in blocks, so cache entries and artifacts can be larger than one request. On plans where Cloudflare refuses request bodies over 100 MB, that is the effective block size.
Blocks per upload50,000More fails with Too many blocks.
Download links1 hour, or 10 minutes from the REST APIThe signed links a job gets to restore a cache or download an artifact, and the redirect from GET .../actions/artifacts/{id}/zip.
Runtime tokenWhile the job runsACTIONS_RUNTIME_TOKEN works only while its job is in progress, and at most until 30 minutes after the job's timeout.

Agent sandboxes

LimitValueApplies to
Sandbox sizeCloudflare Containers standard-1Each sandbox runs in its own container of this instance type.
Sandboxes at once50max_instances of the sandbox container class, across the whole instance.
Open sandboxes per account and repository5A sixth is refused with 429 you have 5 open sandboxes in this repository, the most allowed; close one first. A sandbox counts until it is closed or its credentials expire after 24 hours. Agent task sandboxes don't count.
Start-up waitUp to 3 minutesCreating a sandbox waits up to 3 minutes for its container (longer than usual right after a deploy, while the host pulls the image). If it doesn't start, creation fails with 503 the sandbox container did not start; try again.
Idle time30 minutesA sandbox that receives no requests for 30 minutes is stopped.
Command timeout10 minutes by defaultexec in the REST API (timeoutMs) and sandbox_exec in MCP (timeout_seconds) can set another value. A command that times out returns exit code 124 and timedOut: true.
Command output1 MiB each for stdout and stderrLonger output is cut and the response has truncated: true.
Request body64 MiBLarger exec, read or write requests fail with 413 request body too large.
File read64 MiBReading a larger file fails with 413 file too large.

Agent tasks

LimitValueDetails
Run length6 hoursA run still going after 6 hours is stopped and fails with The run took longer than 6 hours and was stopped.
Installing the runtime10 minutesInstalling Claude Code or Codex in the sandbox.
Goal and follow-up length20,000 characters422 `goal` is longer than 20000 characters (or body for a follow-up).
Plan length20,000 charactersA plan-first run's plan is cut there and ends with … (plan cut at 20000 characters).
Fix runs for failing checks1 per commit, 3 per taskWith Fix failing checks, a fix run gets the last 80 log lines of each failing check. After 3, the task's conversation says the checks still fail. See Verify and land.
Attempts per task1 to 4422 `attempts` must be a whole number from 1 to 4. Each attempt counts toward how many tasks run at once. A task on a pull request's branch runs one. See Best of N.
Custom command length2,000 characters422 `command` is longer than 2000 characters.
Transcript20,000 lines per run, 32 KiB per lineLater output lines of a run aren't stored (Diffinery's own notes still are); a longer line is cut.
Transcript lines per MCP call500agent_task returns at most 500 lines after after.
Tasks running at once per repository1 to 10, default 3Set under Settings → Project. Further tasks, follow-ups and retries queue and start oldest first. Agent reviews don't count. See Projects.
Project memory40,000 characters, last 50 versions kept422 memory is longer than 40000 characters.
Project instructions10,000 characters422 `instructions` is longer than 10000 characters.
Coordinator5 tasks per post, 20 per person and project per 24 hoursA coordinator run lasts at most 30 minutes and 200 model requests, within its token budget (1M by default); each run of a task it starts has its own budget (2M by default). Both budgets are 100,000 to 50,000,000 tokens. See Coordinator.
Project conversation post20,000 characters422 `body` is longer than 20000 characters. The conversation API and project_posts return at most 100 posts per call.

See Agent tasks. For the Today view's limits (repositories active in the last 30 days, at most 100 of them, 50 open pull requests each), see Today.

Agent review

LimitValueDetails
Review length30 minutesThe review stops and its Agent review check fails with "The review didn't finish".
Model requests per review200The model proxy answers 429 to the next request and stops the review; its check fails.
Tokens per review2,000,000 by default, settable from 100,000 to 50,000,000 (tokens_per_review)Counted by the model proxy from the provider's usage (cache reads as a tenth). A request whose booked estimate could pass the budget is refused, and the review stops.
Automatic reviews per pull request10 in 24 hoursLater pushes get an action-required check; people with write access can still ask.
Reviews per pull request20 in 24 hours, asked for or automaticThe check asks for action, and asking returns 429.
Reviews per repository50 reviews and autofix runs in 24 hours by default, settable from 1 to 1000 (daily_limit)The check asks for action, asking returns 429, autofix waits, and the admin whose key reviews use gets a review_limit_reached email at most once a day.
Inline findings per review25Findings outside the changed lines go in the review's summary.
Changed files in the prompt300The rest are summed up as … and N more.
Review rules in the prompt48 KiBOf the base branch's REVIEW.md and .cursor/BUGBOT.md files.
Autofix tasks per pull request3Only for pull requests whose author has write access. Autofix runs count toward the repository's daily limit.

See Agent review.

Container registry

LimitValueApplies to
Request body100 MBCloudflare refuses larger requests. docker push from a hosted workflow splits large layers into chunks automatically; other clients need chunked uploads for layers over 100 MB. Pulls have no size limit.
Manifest size4 MiBLarger manifests fail with 413 MANIFEST_INVALID: manifests are limited to 4 MiB.
Image name255 characters<owner>/<name>, lowercase letters, digits, ., _, - and /.
Tags per tag-list page10,000GET /v2/<owner>/<name>/tags/list?n=.
Packages listed per owner1,000GET /api/v3/users/{owner}/packages, /orgs/{owner}/packages, /user/packages.

Imports

Limits of imports from GitHub, from the web form, diffinery migrate github, POST /api/v3/diffinery/imports and the MCP tool start_github_import.

LimitValueApplies to
Imports at a time1 organization import and 3 repository importsCounted per person or agent who starts them and per account imported into. Organization imports count while draft, queued or running; repository imports while queued or running, not counting the repositories inside an organization import. Over the limit: 429 an organization import is already in progress (at most 1 at a time); wait for it to finish or cancel it or 429 too many repository imports are already running (at most 3 at a time); wait for one to finish.
Repository size10 GB by defaultGitHub's reported size of the repository in a one-repository import, checked before the import starts: 422 the repository is about N GB, over the M GB import limit; ask an administrator to raise IMPORT_MAX_REPO_KB or split the repository. Operators change it with IMPORT_MAX_REPO_KB (see Configuration). Each repository of an organization import is checked too, and one over the limit fails before anything is copied.
Repositories per organization import1,000The repositories list (422 at most 1000 repositories per import), and the repositories read from the GitHub organization or user.
Repositories imported at once1 to 8, default 3One organization import's concurrency (Repositories at a time in the form). 422 concurrency must be between 1 and 8.
Git transfer6 hoursCopying the Git data in the import container. After that the import fails with the Git transfer did not finish within 6 hours. A transfer that fails for another reason is tried once more, except when push protection stopped it.
Size of each push in the transfer32 MiBThe import agent pushes history in chunks of at most this size, below the 100 MB push limit, and smaller ones if a push is still refused as too large.
Retries of each import step5, 15 seconds apart at first and backing off exponentiallyEach step of the import's durable workflow, which may run for at most 15 minutes per call.
Calls per paged step2,000Steps that page through GitHub (issues, comments, reviews and the rest). Each call reads up to 10 pages of 100 items (30 pull requests for reviews). More fails the import with the <step> step did not finish within 2000 pages.
GitHub rate limitsWaits 5 seconds to 1 hour at a timeWhen GitHub answers with a rate limit, the import sleeps until it resets and continues; it doesn't fail.
GitHub responses8 MiB each, 30 seconds per requestA larger response fails with GitHub response is too large or GitHub response exceeded the size limit. Redirects are followed only on the same host.
Organization import duration1 weekAn organization import waits up to 168 hours for its repositories. Any still unfinished then count as not imported, and the organization import fails.
Draft organization imports7 daysAn organization import started in the web form whose repositories were never chosen is cancelled (draft expired) and its GitHub token erased.
Release assets2 GiB eachLarger assets, and assets that can't be downloaded, are skipped with a warning.
Warnings500 per import, 1,000 characters eachFurther warnings are not recorded.
Imports listed100The most recent imports you started, in GET /api/v3/diffinery/imports, list_imports and the Imports page.
GitHub token400 characters, no spaces422 github_token is not a valid token. Deleted when the import ends.
Push token of the transfer1 day, one repositoryThe token the import container pushes with. It works only for Git, Git LFS and the import's own agent endpoints, and is revoked when the transfer ends.
Copied repository details20 topics, 1,000 characters of description, 500 of homepageTaken from the GitHub repository.

Webhooks and events

LimitValueApplies to
Webhook URLhttps:// to a public hostOther schemes fail with webhook URL must use https. localhost, single-label names, .local and .internal names, and private, loopback and link-local addresses fail with webhook URL must be a public host; Diffinery's own host and its subdomains fail with webhook URL can't point at Diffinery itself. A webhook saved before these checks that points at such an address is never delivered to (recorded status 0).
Webhooks per repository20The 21st fails with 422 and a repository can have at most 20 webhooks, as on GitHub.
Delivery timeout10 secondsA receiver that takes longer counts as failed (recorded status 0).
RedirectsNot followedA 3xx answer is recorded as the delivery's status.
Failed deliveriesRetried up to 5 timesA network error, a timeout, 408, 429 or a 5xx status is retried 1 minute, 5 minutes, 30 minutes, 1 hour and 3 hours after successive failures (6 attempts at most, about 4 hours and 36 minutes in all), with the same X-GitHub-Delivery ID. Other statuses, including 3xx and other 4xx, are not retried. A retry is dropped if the webhook was deleted. Each attempt updates the last status and time shown on the webhook. See Retries.
Event processing retries8, then the dead-letter queueIf processing an event fails (audit log, webhooks, notifications), it is retried with backoff (starting at 20 seconds and doubling, at most 5 minutes) up to 8 times, then moved to diffinery-events-dlq.

Email

LimitValueApplies to
Email log retention90 daysSettings → Email and GET /api/v3/diffinery/emails show emails from the last 90 days. Older entries are deleted once a day.
Sending retries6 attemptsA temporary sending failure (rate limit, internal error or delivery failure reported by Cloudflare) is retried with backoff (starting at 30 seconds and doubling, at most 10 minutes). Other failures are logged as failed at once. After 6 attempts the email is logged as failed.
Message body20,000 charactersLonger issue, pull request or comment text in an email is cut and ends with ….
Subject900 characters
Commits in a push email10
Jobs in a workflow run email20
Line comments in a review email20
Mentions per event60Only the first 60 distinct @mentions in one comment or description are notified.
Recipients per event200At most 200 people are notified about one event, in the app or by email.
Invitations50 per person per 24 hoursInvitations to an organization, a repository or the site, counted per inviter. The 51st answers 429 you can send at most 50 invitations in 24 hours; try again later. Site administrators aren't limited.
Test emails5 per account per hourSend me a test email and POST /api/v3/diffinery/emails/test; the 6th answers 429 you can send at most 5 test emails an hour; try again later.
Images in emailsLinked, not loadedAn image in issue, pull request or comment text that isn't on Diffinery itself shows in the email as a link ([alt text]), so opening the email doesn't tell the image's host that you read it.

Platform limits for operators

These come from apps/diffinery/wrangler.jsonc and matter when you run your own instance (see Configuration).

LimitValueApplies to
Worker CPU time300,000 ms per requestlimits.cpu_ms.
Live update connection10 minutes, up to 10 topicsA page's WebSocket is closed with code 4001 after 10 minutes and reconnects at once, so access is checked again.
Runner containersstandard-2, up to 100 instancescontainers[RunnerContainer].
Sandbox containersstandard-1, up to 50 instancescontainers[SandboxContainer].
Import containersstandard-4, up to 20 instancescontainers[ImportContainer]: one container per Git transfer of an import.
Container rollout grace period23,400 seconds for runners, 25,200 for imports, 3,600 for sandboxesrollout_active_grace_period. A deploy doesn't replace a container younger than this, so running jobs (6 hours at most) and import transfers finish first.
Event queueBatches of up to 50 messages (2 seconds), 8 retriesdiffinery-events, dead-letter queue diffinery-events-dlq.
Mail queueBatches of up to 20 messages (2 seconds), 8 retriesdiffinery-mail, dead-letter queue diffinery-mail-dlq. The consumer itself gives up after 6 attempts.
Workflow schedulerEvery 5 minutesSchedulerDO's own alarm (SCHEDULE_TICK_MINUTES, 1 to 60) starts scheduled workflows, up to 16 repositories at a time.
Cron triggerEvery 5 minutes*/5 * * * *: token expiry reminders, email log pruning, container registry cleanup, cancelling draft imports older than 7 days, and setting the scheduler's alarm if it isn't set.