What a repository is

A Thumper repository holds code and weights together. Source files live in text lanes that merge at the character level; large binaries are chunked and deduplicated into the object store. Every listing on the catalog is backed by one repository, and the same address serves both: the public page at /apps/<owner>/<name> and the developer view at /apps/<owner>/<name>/dev. The kinds are app, lib, model, modelpack and dataset.

Repository classes

A repository is created as private plaintext, published public, private end-to-end encrypted, or a public fork. Public repositories are readable by anyone signed in; private E2EE repositories are opaque to the server, which stores and routes authenticated updates it cannot read.

The developer view

TabWhat it showsAddress
OverviewREADME, latest seal, linked listing, stars and watchers/<kind>/<owner>/<name>/dev
CodeFiles at a branch or seal; blame per file?tab=code
IssuesIssues and proposals recorded against the repository?tab=issues
WorkflowsNative check runs (thumper-ctl repo run/list/view)?tab=workflows
ReleasesSigned publications and their files?tab=releases
SecurityAdvisories, franking evidence, protection rules?tab=security
InsightsDependencies and dependents?tab=insights
SettingsGeneral, Listing, Upstream source, Marketplace, Access, Automation, Checks, Lifecycle?tab=settings

.thumper vs .git

Thumper does not replace git. A synced folder can be a git repository at the same time: git keeps its objects in .git/, Thumper keeps its own state in .thumper/, and neither directory is ever uploaded. thumper push walks the working copy, moves the branch's live frontier one file at a time (each update signed by this machine's registered device key) and then seals the frontier into a signed, content-addressed commit — the seal is what Code, Commits and clones read.

PathOwnerBehaviour
.thumper/The working copy's link to its repository: repo.json (server, repository id, owner/name, branch, class) and state.json (last seal, the pushed tree as path → BLAKE3)Never uploaded, never versioned: it is in the default ignore list. Plain JSON — `cat .thumper/repo.json` is the inspection tool. Deleting it only unlinks the copy; `thumper repo init OWNER/NAME` relinks.
.git/A normal git repository, if you keep oneNever uploaded, never rewritten. Thumper reads .gitignore but does not touch git objects, hooks or refs.
.oxen/Oxen's internals, only if you also use Oxen in the same folderIgnored like .git/. Thumper never writes it; its own state stays under .thumper/.
.thumperignoreYour ignore rules for Thumper onlyHighest precedence. gitignore syntax; !pattern re-includes.
.stignore / .gitignoreSyncthing and git ignore files you may already haveHonoured in that order after .thumperignore, before the built-in defaults.
Migrating from git: keep the .git directory, add a .thumperignore for anything you do not want synced, and link the GitHub or Hugging Face upstream under Settings › Upstream source. Pulls land on a draft branch; nothing is published automatically.

Ignore files and file types

Ignore precedence

Four sources are merged, highest first: .thumperignore, .stignore, .gitignore, then the built-in defaults. Inside one file the last matching line wins, so !keep.txt can re-include a file an earlier *.txt excluded. A trailing slash limits a pattern to directories; a pattern without a slash matches at any depth.

.thumperignore
# outputs regenerate; do not sync them
outputs/
*.log
# but keep the one sample the README links to
!outputs/sample.png

Built-in defaults

.DS_Store Thumbs.db desktop.ini $RECYCLE.BIN/ *.swp *.swo *.tmp *~ .git/ .hg/ .svn/ .idea/ .vscode/ node_modules/ .npm/ .yarn/ .pnpm-store/ dist/ build/ target/ .venv/ venv/ __pycache__/ *.pyc .pytest_cache/ .mypy_cache/ .ruff_cache/ *.egg-info/ .tox/ .thumper/ .oxen/

How file types are stored

There is no extension list to configure. Thumper decides by content and size: text files become text lanes, everything else is chunked bytes. The rules below are the shipped constants.

KindStorageLimits
Source and config (text)One text lane per path inside the branch document; character-level merge; blame per sealVersioned in workspace history up to 25 MiB per file.
Weights, datasets, media (bytes)Content-defined chunks: 8 KiB minimum, 64 KiB target, 128 KiB maximum; identical chunks are stored onceStored in the object store, referenced by manifest; never diffed as text.
Binary files pushed with the CLI (images, small weights)One blob lane per path: the branch holds a BLAKE3 pointer, the bytes live in the repository's content store and are stored once per digestUp to 16 MiB per file and 256 MiB per tree; cloned and pulled byte-identical; shown inline in the Code tab when they are raster images.
App-scoped sealsA manifest may raise its per-file cap, clamped to 100 MiBThe total seal budget still applies.
Conflicting edits from two devicesKept side by side, never overwrittenThe loser is renamed <stem>.sync-conflict-<timestamp>-<id>.<ext>.

Heads, seals, branches

Every branch has two heads. The live frontier moves on every edit from any authorised device or agent. The sealed head is an immutable seal: a signed commit with parents, a content digest, and provenance (who, from which device, under which capability). Auto-checkpoints between seals are cheap and disposable; a seal squashes the run since the last seal into one named commit.

ConceptGit analogueNotes
live frontierworking copyshared, continuous; there is no private copy to push
auto-checkpointjj's @ changefrontier marker per agent turn or idle debounce; not a GC root
sealcommitcontent-addressed, signed, single linearisation point per ref
refbranch or tagmoved by compare-and-swap; a stale head answers 409
proposalpull requestrecorded under Issues; merge itself is behind TR_REPO_MERGE_ENABLED
Fork and promote are shipped behind TR_FORK_ENABLED (the Fork button on the developer view). Pull from a fork is not exposed yet; a mirror's upstream is browsed read-through in the Code tab until its staged draft is published.

Publishing and listings

A listing is a catalog row that points at a repository. Publishing a listing without signed publication files is allowed: the page exists, the download button does not. Signed publications appear under Releases and make the app installable from the desktop app.

Settings panels

General (default branch, handle), Listing (name, category, screenshots, README image policy), Upstream source (link a GitHub or Hugging Face repository; pulls are staged as drafts every hour), Marketplace (list for sale), Access (collaborators and invitations), Automation and Checks (checks that must pass), Lifecycle (make public or private, archive, delete).

README, licence and stars on the public page

The public page renders the upstream README with images stripped by default (the publisher can allow images from the upstream repository, or all images), the SPDX licence, and the upstream star count. All three refresh on the hourly upstream poll.

API and CLI

Repository routes live under /api/v1/repos. Every route checks the session, the repository's visibility and the caller's capability before it does anything; a repository you may not see answers the same 404 as one that does not exist. Mutations with a bearer token must be request-bound.

RoutePurposeRequires
GET /api/v1/public/{kind}/{owner}/{name}Public coordinates of a listing or repositoryAnonymous
GET /cli/install.sh, /cli/install.ps1, /cli/release.json, /cli/download/{artifact}CLI distribution: Linux builds from this server's image plus the macOS, Windows and Linux-aarch64 builds the release workflow publishes, each with size, SHA-256 and the commit it was built from (what the installers and `thumper upgrade` use)Anonymous
POST /api/v1/auth/cli/device-code, /approve, /tokenthumper login: device-authorization grant; the browser approves, the CLI receives a session sealed to its transport keyPublic start/claim; approval needs the browser session
POST /api/v1/reposCreate a repository (name, class, default branch)Session
POST /api/v1/repos/device-key-challenges, /device-keysRegister this machine's Ed25519 device key (X25519 challenge, proof of possession)Session
POST /api/v1/repos/{id}/branches/{b}/live/bootstrap, /proof-context, /updatesLive branch: snapshot + replica id, the signing inputs for one file change, then the device-signed Loro updateSession; TR_REPO_LIVE_TEXT
PUT / GET /api/v1/repos/{id}/payloads/{blake3}Blob lane: content-addressed bytes for binary files (≤16 MiB each); the branch carries only the pointerSession; write capability; not for private E2EE repositories
POST /api/v1/repos/{id}/branches/{b}/seal/prepare, /finalizeTwo-phase seal: the server returns the exact bytes, the device key signs themSession; TR_REPO_SEALING
GET /api/v1/repos/{id}/commits, /tree/{sha}, /blob/{sha}/{path}, /raw/{sha}/{path}, /archive/{sha}.tar.gzHistory, tree, file, raw bytes and the whole tree as one tarball at a seal (what the Code tab, `thumper repo clone` and `git clone` read)Anonymous for public repositories
GET / PATCH / DELETE /api/v1/repos/{id}Read, update, soft-deleteSession, owner for writes
GET / POST /api/v1/repos/{id}/releases/{v}/assets, GET / DELETE …/assets/{name}Release assets: list, upload (raw body, ≤128 MiB), download, removeAnonymous reads for public repositories; writer for uploads
GET / POST /api/v1/repos/{id}/refsBranches and tags; creating a ref validates the head in the same transactionSession
GET /api/v1/repos/{id}/logSeal history for a ref (`?unverified=1` for ids and messages without per-seal verification)Session
GET /api/v1/repos/{id}/history-pack?from=&limit=&skip=A slice of the ancestor walk from a seal in one download: manifest.jsonl + every blob once (what git clone uses); `skip` continues the same walk, so side branches behind merges are never lostPublic read
GET /api/v1/repos/{id}/seals/{seal_id}One seal with its provenanceSession
GET /api/v1/repos/{id}/seals/{seal_id}/blame/{path}Blame at a sealSession; TR_REPO_SEALING
GET /api/v1/repos/{id}/compareCompare two refs or sealsSession; TR_REPO_SEALING
POST /api/v1/repos/{id}/merge-preview, /mergeGuarded two-parent mergeSession; TR_REPO_MERGE_ENABLED
PUT / DELETE /api/v1/repos/{id}/star, /watchStar or watch a repositorySession
GET /api/v1/social/{listing_id}Star and watch counts of a listingAnonymous; TR_SOCIAL_ENABLED
GET /api/v1/git-proxy/{host}/{path}Same-origin relay for browser-side git clone of an allow-listed upstreamTR_GIT_CLONE_ENABLED

The thumper CLI

Sign in once per day from the terminal: thumper login prints a key fingerprint and opens /cli/authorize in your browser; approving there gives this terminal a session that is a child of your browser session (same account, same limits, expires with it). The first push registers the machine's device key; every change and every seal is signed with it — no password or token ever sits in the terminal.

bash
# install (Linux, macOS): picks the build for your platform from /cli/release.json and verifies its SHA-256 first
curl -fsSL https://app.thumper.run/cli/install.sh | sh
# install (Windows PowerShell)
irm https://app.thumper.run/cli/install.ps1 | iex
# from source, any platform: cargo build --release -p thumper-ctl --no-default-features
# later: thumper upgrade --check / thumper upgrade
# sign in (device code; the browser approves)
thumper login
# create a public repository and link this folder
thumper repo create my-app --public
# or link a folder to a repository you already have
thumper repo init OWNER/NAME
# push: signed per-file updates, then a signed seal
thumper push -m "first import"
thumper repo status
# clone the sealed tree of a public repository
thumper repo clone OWNER/NAME [-b BRANCH]
# or plain git through the remote helper the installer links (one git commit per seal, one seal per pushed commit; lightweight tags too)
git clone thumper://OWNER/NAME
git remote add thumper thumper://OWNER/NAME && git push thumper main v1.0.0
# this machine's device key, and raw API calls with the session
thumper repo device status
thumper repo api /api/v1/handles/me
# bring the sealed head down into a linked folder (refuses to overwrite local edits without --force)
thumper pull
# branches, proposals and issues from the terminal
thumper repo branch feature-x --from main
thumper pr create -R OWNER/NAME --head feature-x --base main -t "Add X"
thumper pr diff -R OWNER/NAME 7
thumper pr checkout 7 # switch the working copy to the proposal's branch and pull it
thumper pr checks 7 && thumper pr view 7 --web
thumper repo tag create v1.0.0 --branch main && thumper repo tag list
thumper repo branch-delete feature-x # never the default branch
thumper repo edit -d "What this repository is" --default-branch main
thumper repo protect set main --require-review --lock-deletions --required-approvals 1
thumper repo collaborator add HANDLE --role write # admin | write | triage | read
thumper repo access list # grant / decline requests by id
thumper pr merge -R OWNER/NAME 7 --delete-branch # fast-forward the base (a seal signed by this device), accept, drop the branch
thumper pr merge 7 --squash # base moved: three-way merge sealed once (conflicts are listed, never auto-resolved)
thumper pr merge 7 --rebase # base moved: each branch seal replayed onto the base, messages kept
thumper pr merge 7 --merge # base moved: two-parent merge seal via the server's merge lane (gh's default; needs branches that edited different files)
thumper pr update-branch 7 # merge the base INTO the branch first, so review sees current base content
thumper pr review 7 --approve -b "LGTM" # approvals count toward protected-branch rules
thumper issue create -R OWNER/NAME -t "Crash on start"
thumper label create bug -c E60076 && thumper issue label add 12 bug
thumper issue edit 12 -t "Crash on start (Linux)"
# repository administration
thumper repo list
thumper repo visibility private --repo OWNER/NAME
thumper repo fork OWNER/NAME
thumper repo release create 1.0.0 -R OWNER/NAME --commit <seal>
thumper repo release upload 1.0.0 dist/app.tar.gz && thumper repo release download 1.0.0 -D ./out
thumper repo webhook create https://example.com/hook -R OWNER/NAME --events push,release
thumper --yes repo delete OWNER/NAME
# native check runs recorded against a repository
thumper repo run list --repo OWNER/NAME --limit 20
thumper repo run watch --repo OWNER/NAME 42 --timeout 600
thumper repo run rerun 42 -R OWNER/NAME && thumper repo checks list -R OWNER/NAME

git and gh coverage

You knowThumperNotes
git clone / fetch / pull / pushgit clone thumper://OWNER/NAME · git push thumper maingit-remote-thumper (installed with the CLI): one commit per seal, one seal per commit; two-parent merge seals import as git merge commits (a pushed git merge commit becomes a single-parent seal — real merges are made with pr merge --merge); branches and lightweight tags; annotated tags are pushed as lightweight; symlinks and submodules are refused and the executable bit is not stored (the tree has no file modes)
git push --deletegit push thumper --delete NAME (or thumper repo branch-delete NAME · thumper repo tag delete NAME)deletes the branch or tag on the server; the default branch and protected patterns are refused, and the seals stay reachable by id
gh auth login / logout / statusthumper login · logout · auth statusdevice-code flow; the session is a child of your browser session
gh repo create / clone / view / list / rename / edit / delete / forkthumper repo create · clone · view [--web] · list · rename · edit · delete · fork
gh repo set-default, visibilitythumper repo edit --default-branch · repo visibility
gh repo archive / unarchive / deletethumper repo archive · unarchive · delete · restore IDan archived repository refuses every write — push, git push and pr merge — until it is unarchived; delete prints the id that restores it while the purge window is open
Repository transferthumper repo transfer HANDLE [--org] · transfer-list · transfer-accept IDownership moves only when the recipient accepts
Collaborator invitations (invitee side)thumper repo invite accept · declinethe owner's side is repo collaborator add · remove · role
gh search reposthumper repo search QUERY · repo search --trending · --recent · repo list --owner HANDLEpublic search needs no session
Security reports and advisoriesthumper repo reports list · decide ID --triaged|--published|--dismissed; thumper advisories [ID]only a repository's owner triages its reports; advisories are public
Marketplace crosslinkthumper repo listingthe listing linked to a repository, if any
Private paths / paid pathsthumper repo paths list · set PATH --public|--private|--open [--price-cents N] · remove · timelinethe blob, tree and history-pack routes hide private paths from non-writers
Forks, dependencies, seal artifactsthumper repo forks · deps · artifacts SEAL
gh auth tokenthumper auth token list · revoke IDtokens are created in Settings → Tokens
gh ruleset (branch protection)thumper repo protect list · set · clearrequire-review, require-checks, lock-writes, lock-deletions, required approvals; a reviewer who requested changes blocks acceptance on a branch that requires review
Collaborators / access requeststhumper repo collaborator list · add · remove · role; thumper repo access list · request · grant · declineinvites by @handle; roles admin · write · triage · read
Download ZIPGET /api/v1/repos/{id}/archive/{sha}.tar.gz · Code tab → Clone → Download
gh pr create / list / status / view / diff / checkout / checks / review / merge / close / reopen / edit / commentthumper pr … (same verbs, plus update-branch; create --fill; list --author @me -L N; checks --watch --exit-status; review --approve | --request-changes | -b)merge fast-forwards as a seal signed by your device; when the base has moved, --merge produces a true two-parent merge seal through the server's merge lane (command authority + your device signature), --squash merges three-way and seals once, --rebase replays each seal; conflicts are refused with the file list and nothing lands
gh pr create --draft / gh pr readythumper pr create --draft · pr ready [--undo]a draft cannot be approved or merged; the author or a writer marks it ready
gh issue create / list / status / view / edit / close / reopen / commentthumper issue … (same verbs)issue status shows the ones you opened and the open ones by others
Reactions (gh has none — `gh api` only)thumper issue react N [+1|-1|tada|heart|rocket|eyes] [--remove]; thumper pr react N …the six the server accepts; no argument lists the counts and marks yours
gh label list / create / edit / delete / clone; gh issue edit --add-labelthumper label list · create · edit --name-to/-c · delete · clone SOURCE [--force]; thumper issue|pr label add · remove [--create]deleting a label detaches it everywhere
gh release list / create / view / edit / delete / upload / downloadthumper repo release list · create · view · edit --notes|--notes-file · delete · upload · download · delete-assetassets up to 128 MiB each, stored once by BLAKE3 in the repository content store; edit rewrites the notes only — a release names an exact commit
gh run list / view / watch / cancel / rerunthumper repo run list · view · watch · cancel · rerun · createruns are the canonical check set recorded per commit, not arbitrary Actions YAML; no log download
gh workflow enable / disablethumper repo checks list · enable · disable
gh apithumper repo api [-X] [-d] PATHrequest-bound session bearer
gh secret / variable—checks are recorded, not executed, so there is nothing to inject secrets into
gh browsethumper repo view --web · pr view --web · issue view --web
Anonymous reads of public repositoriesrepo view · clone · branch · tag list · checks list · pr/issue list · git cloneno session needed; suspended or killswitched repositories stay offline for everyone
gh completionthumper completions <shell>
gh upgrade (brew/apt)thumper upgrade [--check]verified against /cli/release.json
Pushes carry UTF-8 text files up to 256 KiB each as text lanes and every other file up to 16 MiB as a blob lane (a BLAKE3 pointer inside the branch, bytes in the repository's content store; 256 MiB per tree). Both honour .thumperignore with the same rules as the sync engine; files over the caps are reported as skipped rather than silently dropped. Files removed locally are removed from the branch unless you pass --no-delete; a push that would overwrite a seal you have not pulled is refused (see thumper pull).

Feature flags

Each surface is behind an environment flag on the node. A route behind a flag that is off answers 404, indistinguishable from a route that does not exist. /api/v1/client-config reports the flags a client should honour.

FlagEnablesapp.thumper.run
TR_REPOS_ENABLEDMounts /api/v1/repos and the developer viewon at app.thumper.run
TR_MIRROR_SYNC_ENABLEDUpstream links: poll GitHub or Hugging Face, stage drafts, refresh README and starson
TR_GIT_CLONE_ENABLEDBrowser-side git clone through the same-origin relayon
TR_FORK_ENABLED, TR_TIME_MACHINE_ENABLEDFork button and the Time Machine viewon
TR_REPO_SEALINGSigned seals (thumper push), compare and blameon
TR_REPO_MERGE_ENABLEDMerge preview and mergeoff
TR_REPO_LIVE_TEXTLive branch updates (thumper push, live text lanes)on
TR_LAKEFS_S3_CONDITIONAL_PUTnative (If-None-Match) or emulated put-if-absent for object stores that answer 501, e.g. Backblaze B2emulated
TR_SOCIAL_ENABLEDStars and watchers on listing pagessee /api/v1/client-config