DOCUMENTATION
How CodeRook works
The desktop application, the command line and this website are three doors into the same thing: one account, the same projects, the same rules about what gets sent.
How it works
A project is a folder on your machine connected to a project on your account. A save is everything in that folder at one moment — not a list of edits since last time, but the complete set of files. Every save is a commit; one you give a name to becomes a version, and versions are what the project offers to anybody outside it.
That distinction is why restoring is safe. Going back to save 4 does not mean undoing 5 through 12: save 4 already names every file it had, so it can be written out on its own. Nothing depends on the saves around it.
Nothing is sent twice
Files are stored by the digest of their contents, so one that appears in fifty versions is stored once. Saving sends only what the service does not already have.
Your folder stays yours
CodeRook writes the files a version holds and nothing else. Dependencies, local credentials and your git repository are never touched.
It knows what it gave you
The exact copy placed in your folder is remembered, which is how a file you edited is told apart from one left behind by somebody else’s change.
Getting started
- Create an account, or sign in with Google or GitHub.
- Install the desktop application from the download page, or the command line with
npm install --global @coderook/cli. The package is named for the host; the command it installs iscbx. - Point it at a folder — open it in the application, or run
cbx sign-inand thencbx statusinside it. Either way you are shown what would be sent before anything leaves the machine. - Settle the rules once: what belongs in the project and what does not.
cbx ignore --suggestnames the dependency folders, virtual environments and build output it can see, and what each is costing you. - Make your first save — the Save button, or
cbx submit -m "First pass".
Choosing what gets sent
CodeRook reads .gitignore. Not a format of its own: the same file git already reads, so a project that is set up needs nothing new, and a rule added for one is understood by both.
# .gitignore node_modules/ __pycache__/ .venv/ *.log dist/ # bring one thing back out of an ignored folder !dist/release-notes.md
Nested .gitignore files apply to their own directory downwards, and a ! rule can reach into a folder that is otherwise excluded. Personal exclusions you would rather not share go in .git/info/exclude, which is never uploaded.
One exception, and it is git’s own: in a folder that is a git repository, a file git already tracks is sent even if a rule matches it. Git applies .gitignore only to files it is not tracking yet, so a broad rule like storage/ leaves a committed internal/storage/ alone there — and here too, rather than arriving without the files that made it build. The desktop application lists them as tracked by git where a rule would be.
Git’s own .git directory is never sent. Every client skips it, and the service refuses any save with a path inside one, because a project fetched into somebody’s git checkout must not be able to write over that checkout’s settings.
Files that look like credentials — .env, .pem, .key — are pointed out before anything is sent, wherever they sit in the tree. They are not silently dropped either: an incomplete project that looked backed up is its own kind of disaster.
A key that gets past that is refused by the service itself, not only by the app you are using — the same answer whether it came from git push, the command line or the website. If you publish one on purpose, everybody outside the project sees that file with the value covered over: same name, same size, same variable names, asterisks where the key was. Only the owner and the project's admins can download it intact, through the Download with secrets button or a signed-in client. The security page sets out who counts, what covering does and does not reach, and how it was tested.
Keeping files private
A public project can keep chosen files and folders to its own people: design sources, art, internal notes. Name them in the project’s Settings, under Private files, or from the command line. Patterns work like a .gitignore.
cbx private add "design/" "*.psd" # a folder anywhere, a file type cbx private add "docs/internal" # a path from the top of the project cbx private list cbx private remove "*.psd"
Everybody outside the project is then served it without those files, in every version, old ones included: the file list, a file by its path, the README, zips, comparisons, git clone, Pages and search all leave them out, and a folder holding nothing else is not listed either. The project’s own people see everything, as before. The list is kept on the project rather than in a file inside it, so a save can never undo it, and only the owner or an admin can change it.
Two things still count private files: the project’s total file count and size, and a folder’s “last change” date. Neither names a file or shows what is in it.
Saving
In the desktop application you see every changed file and its diff before anything leaves the machine, and choose which belong in this save. Pressing Save sends it; the application stops to ask again only when the upload would leave the account close to its storage or its monthly upload allowance. The command line sends whatever the rules allow, so cbx status and cbx diff are how you look first. A note is optional; the save is complete either way.
cbx status # what is here that is not saved cbx diff # what changed, line by line cbx submit -m "First pass" # send it as a save
If the connection drops after the service has taken the version but before your machine hears so, running the same command again finds the save that was made rather than making a second one.
Catching up
get brings your folder up to date. It writes the files the version changed and leaves everything else exactly as it is — including work you have not saved yet.
If a file you are editing would be written over, it stops and names it rather than proceeding. Saving first is the way through that loses nothing.
cbx get # bring this folder up to date cbx get --replace # exact copy, discarding local changes
When two people save at once
Nobody is told to try again, and nobody’s upload is thrown away. What happens depends only on whether the two changes actually disagree.
Different files
Combined. Neither of you is asked anything.
Same file, different parts
Combined line by line. Still nobody is asked.
The same lines
Your upload is kept as a merge and the project is left alone until somebody decides. Both sides exist the whole time; neither is overwritten while the question is open.
cbx merges # uploads waiting on a decision cbx merge M-1 # look at one cbx merge M-1 --mine # keep your side, or --theirs, or --both
On the website and in the desktop application a waiting merge shows both sides of every disagreeing file, line by line, with the agreement between them folded away. Picking a side is one answer; the other is to write the file yourself, which is the right one whenever the result is some of both.
Before a long upload the command line asks whether it is walking into one of these, so you hear about it in the first second rather than after everything has been sent.
Pull requests
Work on a line of its own gets back to main by being proposed: a pull request (a proposal) offers the line to main, to be checked and reviewed before it lands. It follows both sides, so saving onto the line after proposing it changes what is proposed rather than needing a second one. Files both sides changed are combined line by line where they can be, and anything left is the same decision two colliding saves would be, shown the same way.
cbx track spike --new # a line of your own cbx submit -m "Try the other encoder" cbx propose -m "Faster encoder" # offer it to main cbx proposals # what is open cbx merge M-3 --ask sam,priya # ask people to review it cbx merge M-3 --apply # land it, once nothing is in the way
On the website it is Propose a line on the Merges page; in the desktop application, propose beside the line you are saving to. Either way the people asked to review are told, and whoever proposed it hears when it is reviewed, when a check fails and when it lands. “Fixes #12” in the title or description closes issue 12 when the proposal lands.
A line can ask for approvals before anything lands on it, set per line in the project’s settings. An approval from whoever proposed it does not count, and on a line that asks for approvals, one person asking for changes holds it until they approve.
The Pull requests tab
Every project has a Pull requests tab beside Issues: open, merged and closed, with search and Waiting on my review. Each pull request has four tabs. Conversation is the description, comments and the record of what happened: saves pushed, reviews, review requests, labels, renames, drafts. Saves lists the line’s saves since it left its target. Checks shows every check and whether it is required. Files changed shows each changed file line by line, with a “Viewed” tick that clears when the file changes again.
Press + beside a line to comment on it. A comment on a line starts a conversation that can be replied to and marked resolved; tick Suggest a change to propose replacement text, which the author applies as a save with one press. Beside the conversation: who was asked to review and where each stands, labels, the issues it closes when merged, and whether you hear about it. A box at the end says exactly what stands in the way of merging — a check that has not finished or is failing, approvals still needed, somebody asking for changes, files to settle, a draft — or that nothing does.
An approval is of the code somebody looked at: when new saves land on the line, earlier approvals stop counting and the record says so. A request for changes stays until its reviewer moves. A draft is shared but cannot be merged until it is marked ready. Whoever opens, comments on, reviews or is asked to review a pull request hears about it from then on, and so does anybody it mentions with @name.
cbx pr list # this project's pull requests cbx pr view M-3 # description, reviews, checks, what blocks it cbx pr files M-3 # what it changes cbx pr comment M-3 --line src/app.ts:42 -m "This can be null" cbx pr approve M-3 # or: cbx pr request-changes M-3 -m "…" cbx pr label M-3 bug,ui cbx pr ready M-3 # or: draft, close, reopen, retarget M-3 release cbx pr merge M-3 # or --squash cbx pr auto-merge M-3 # merges by itself once nothing stands in the way
What a line insists on
A line can be protected (changes only through pull requests) and can ask for approvals, under Lines in the project’s Settings. Under Merge rules it can also name checks it insists on, so a pull request whose CI never ran cannot land as though it had passed, and it can require the code owners of what a pull request changes to approve it. Code owners come from a CODEOWNERS file (.rook/CODEOWNERS, or CODEOWNERS at the top) on the default line: a pattern, then usernames. They are asked to review whatever touches their paths.
# .rook/CODEOWNERS * @sam /docs/ @priya *.rs @sam @alex
Merge when ready on a blocked pull request (or cbx pr auto-merge) lands it by itself, as you, as soon as nothing stands in the way. Squash lands it as one save without the line’s own saves in the target’s history.
Checks come from actions. One set to run on proposals runs when a line is proposed and again on each save to it, and its result is a required check on the proposal; a new save replaces the old verdict rather than leaving a pass up for code that has since changed. The same switch decides whether an action runs on every save or only when somebody starts it.
cbx actions --on-proposal Tests # its result becomes a check cbx actions --on-save Build # runs on every save cbx actions --by-hand Deploy # only when somebody starts it
Every proposal also has a coderook/malware check. What the line adds or changes is scanned before it can land, and a finding names the file and fails the check, which stops the proposal being applied. Files already scanned are not sent again. If the scanner cannot answer, the check says so and is skipped rather than holding the proposal.
Anything else can report a check too, with PUT /v1/merge-tracks/{id}/checks/{name} and {"state": "passing", "detail": "…", "detailsUrl": "…"}, using a token that reaches only that project.
Protecting a line is what makes this the only way in. A protected line refuses a save made straight onto it — the command line, the desktop application and git push alike, and before a long upload rather than after it — so changes arrive by proposal. It also cannot be put back on an earlier save, and a merge cannot land on it over an unresolved conflict or a failing check. The project’s owner and its admins set this per line, in the project’s settings.
The pull-request assistant
Pull requests are manual unless a repository's admin turns on CodeRook's assistant in Settings → Automation. It comes with a subscription or storage add-on on the owner's account. Set to Automated, it works each pull request when it opens or gets a new save, doing only what you switch on:
- Review changes: notes on the changed lines, shown under Checks as the assistant's activity, never as a person.
- Write the description: when the author left none.
- Suggest fixes: a note may carry a replacement that anyone who can change the line applies with one click.
- Approve or request changes: its verdict becomes its own check. It never counts as one of a line's required approvals.
- Merge when it approves: it turns on auto-merge, which lands only once approvals, required checks and protection all allow.
While it is on, the files a pull request changes are sent to the assistant's model through OpenRouter, only to zero-retention providers that neither keep nor train on them. Credential files are never sent. See AI on CodeRook and the subprocessors.
Each account gets a monthly assistant allowance of a fifth of what it pays: Pro covers about US$1.40 of assistant work a month, Power US$4.00, and every US$4 storage block adds US$0.80, pooled across all its repositories. A small pull request's review costs around a tenth of a cent, so that is hundreds of reviews. Settings shows how much of the month is used; once it is all used, reviews pause until the next month while pull requests carry on as usual. One review stops at US$0.05 however large the pull request.
Issues
Anybody who can read a project can open an issue and reply; labels, assignment and closing need write access. An issue keeps a record of what happened to it between its comments: labels added and removed, who it was given to, a new title, and every other issue or pull request that mentions it by number (#12). Closing says why: completed, not planned, or a duplicate of another issue. The list filters by label and assignee, and shows who opened each one.
cbx issue view 12 cbx issue comment 12 -m "Fixed in v41" cbx issue close 12 --duplicate-of 9 # or --reason not-planned cbx issue label 12 bug,ui cbx issue assign 12 sam cbx issue labels --starter # bug, enhancement, question and five more
Issue forms
A form asks the questions you need answered before anybody can act on a report. Save YAML files in .rook/issue-forms on the default line and New issue offers them; the answers become the issue’s text, one heading each, and the form’s labels are added.
name: Bug report
description: Something is broken
title: "[Bug] "
labels: [bug, triage]
fields:
- type: textarea
id: what
label: What happened?
required: true
- type: select
label: Which platform
options: [Windows, macOS, Linux]
- type: checkbox
label: I searched for an existing issueIssue rules
Under Issue rules in Settings: add a label when an issue’s title or text says certain words; give issues with a label to whichever of a few people has fewest open; welcome somebody’s first issue with a message; and mark issues that have been quiet for a while, closing them as not planned some days later unless somebody replies. Each rule is a condition you write and one action, and replies a rule writes are marked as automated. Labels and descriptions are managed under Labels.
Forks and outside contributors
Anybody signed in can fork a public project from its page, or with cbx fork owner/project: a private copy under their own account with the project’s latest public version (its private files left out). Nothing is uploaded again; the copy shares what is already stored.
cbx fork sam/editor # your own copy cbx fork offer fix-export --title "Fix the export dialog" cbx fork sync # the original's latest, on an "upstream" line
Offering a line puts it into the original as forks/you/line and opens a pull request from it; offering again moves that pull request to your newest save. The project’s people review and merge it as any other. Its actions run only once somebody on the project approves each run, and they get none of the project’s secrets.
Reporting a vulnerability
Report a vulnerability on a public project’s page (or cbx security report) sends it privately: only the project’s owner and admins, and you, can read it. They are told at once, can reply, and mark it accepted, fixed or declined; you hear each time. A project can say what it wants to hear about, or stop taking reports here, under Security reports in its Settings.
cbx security report sam/editor --title "Path traversal in export" --severity high cbx security list cbx security reply S-3 -m "Confirmed; fix in 2.0.1" cbx security mark S-3 fixed
The command line
The same engine as the desktop application — the same scanner, the same rules, the same uploads — with a terminal in front of it. A project moved by one is understood by the other.
The command is cbx, and the split it names is the one git and GitHub have. CBX is the engine: content-defined chunking, whole-snapshot versions, the .cbx format. CodeRook is the host it talks to. So you install a package called @coderook/cli and the thing you type is cbx — the same way installing git does not give you a command called GitHub.
npm install --global @coderook/cli cbx sign-in cbx projects # everything on your account cbx clone my-project # fetch one into a new folder cbx doctor # check the service, config and sign-in
coderook still works as another name for the same program, so anything written before the rename keeps running.
Set CODEROOK_TOKEN and it needs nothing on disk, which is what makes it usable from continuous integration.
Lines of work are here too, so a terminal is not stuck saving to the default. Switching says where the next save goes and nothing else — no files move and nothing is fetched, so run cbx get afterwards to bring that line’s files into the folder.
cbx tracks # the lines this project has cbx track spike --new # start one and switch to it cbx track main # switch back cbx submit --track spike -m "Try the other encoder"
The line is remembered per folder rather than per account, so two checkouts of one project can sit on different lines. A name that does not exist is refused rather than created quietly, because a line called mian puts work somewhere nobody will look for it.
It will also tell you what a folder is carrying that probably is not the work — dependency directories, virtual environments, build output — with how much each is costing you. The desktop offers this when a folder is added; the command line asks for it.
cbx ignore --suggest # list them cbx ignore --suggest --apply # add the confident ones
An issue can be filed with its labels and the save it is about in one go. “It broke” and “it broke in v41” are different reports, and the second is the one somebody can act on.
cbx issues # what is open cbx issues --new "Installer will not run" \ --labels bug,windows --version 41
A project can also tell something else when things happen here — a build box, a chat room, a status page. The signing secret is made by the service and shown once, when the hook is added; no route hands it back afterwards, so check the signature on what arrives and nobody else can post to your endpoint.
cbx hooks # where it tells cbx hooks --add https://example.com/coderook cbx hooks --add https://example.com/builds \ --events version.published
A history on this machine
A folder can also keep its own history, with no account and no connection. cbx init starts one in a .cbx folder; after that you save, compare, branch and merge offline, and push to CodeRook when you want it there.
cbx init cbx save -m "First playable build" cbx switch -c new-movement # a line of your own; no files change cbx merge new-movement # bring it back, line by line cbx push # each save becomes a save here cbx pull # and back, merging if both moved on
A merge takes whatever only one side changed, merges text files both changed line by line, and marks only what is left. Nothing that writes to the folder overwrites a change you have not saved.
Open source
CBX and the cbx command line are open source under the Apache License 2.0, at github.com/AlexC1991/cbx and on CodeRook at batty-251/cbx. The storage format and the sync protocol are written down there, with test vectors, so other tools can read and write the same histories. The engine is also a library: npm install @coderook/cbx.
Releases and downloads
A release is not a separate thing you create. Every save is a commit; giving one a name makes it a version, and only versions are offered to anybody outside the project. So publishing is one command, and it does not upload anything — the files are already there.
cbx release v1.0 # name the newest save cbx notes 41 --edit # what changed, in your editor cbx releases # what has been published, and who can take it
Drafts, channels and signed downloads
A release can be saved as a draft first: its name, notes and channel are kept and builds can be attached, and nothing is public until it is published. Write from what changed fills the notes from the pull requests merged, issues fixed and saves made since the last release. A release on a channel other than stable, or marked as a pre-release, is offered only to apps that ask for that channel (?channel=beta on the update feed).
cbx release v2.0 --draft --generate-notes cbx attach installer.exe 41 cbx release publish --version 41 cbx release v2.1.0-beta.1 --prerelease --channel beta
Every public release has a SHA256SUMS file and a manifest of its files signed with CodeRook’s release key (Ed25519, at /v1/public/release-signing-key). Check a download before running it:
cbx verify sam/editor v2.0.0 ./editor-setup.exe sha256sum -c SHA256SUMS
Lockfiles in a save (package-lock.json, yarn.lock, requirements.txt, poetry.lock, Pipfile.lock, Cargo.lock, go.sum, composer.lock, Gemfile.lock) are checked against the OSV database of known vulnerabilities: on pull requests as the coderook/dependencies check, on any save from History’s Check dependencies, and with cbx deps. Only package names and versions are sent.
On the website it is Make public on a save in the project’s History tab, and Make private takes the name off again. The desktop application’s Commit history window calls the same two things Make a version and Back to a commit. Nothing is deleted either way; the save stays in the history.
A version can also carry files that were never in the project: an installer, a signed binary, a changelog. Attach them from wherever they were built.
cbx attach ./release/Installer-1.0.exe cbx attach ./release/app.dmg 41 # onto version 41
Large files go up in parts, so a 95 MB installer works the same way a text file does. Attaching to a commit is not publishing: the download exists and nobody outside the project can reach it until that commit is named.
Once a release is public its files are fixed. A file cannot be removed from it, and files can be added only for a day after publishing (naming a release and then attaching its builds is the ordinary order). To change one after that, make it private, change it, and publish it again. Apps that update themselves are offered the release with the highest version number, not the most recently named, and a pre-release such as 2.1.0-beta.1 only while there is no full release. Each file shows how many times it has been downloaded.
Every attached file is checked for malware (ClamAV, with signatures refreshed through the day) before anybody outside the project can download it, usually within a minute. Until then a download answers that it is being checked; the project’s own people can take it straight away. A file that fails is never offered again: it disappears from the release, the latest-release document and the update feed, and whoever attached it and the project’s owner are told what was found. Listings say where each file stands as scan: clean, checking or unchecked.
Releasing downloads without the source
A public project does not have to publish its source to publish its app. Make a release’s downloads public on their own and its notes, everything attached to it and the update feed are offered to anybody, while the version’s files keep their own visibility. One project holds the code and ships the installers; there is no need for a second project that only holds releases.
cbx release 1.4.0 --downloads public # name it and offer its downloads cbx release --downloads private --version 41 # take them back
On the website it is Make downloads public in the Who can download panel of a release in the project’s History, which also shows the addresses an app or a script reads it from. A visitor sees the release on the project’s Releases tab, marked Downloads only, and the project’s Code tab says the source is not public. Every way into the files themselves stays closed: the file list, a single file and the version’s zip all answer as if they did not exist.
An action that keeps files can do this itself. Turn it on once and every passing run hands what it built to the version it ran against.
cbx actions # what each one keeps cbx actions --ship "Windows installer"
Worth being deliberate about, because attaching is publishing: anything an action ships onto a public version is a download anybody can take, from the moment the run passes. It is off until you say otherwise, and the same switch sits on the Actions screen for anyone who would rather click it.
Building for every platform from your machine
cbx build makes your project’s executables for Windows, Linux and macOS on the computer you are using, with the tools it already has. Nothing is uploaded unless you ask, and no container on our side is involved.
cbx build --plan # how each target would be built here cbx build --target win,linux,mac # build them cbx build --target all --ship # and attach them to the newest save
Each target is built natively or cross-compiled, chosen from what is installed — Docker and WSL are used when present and never required for Linux. One that cannot be built on this machine is named, with what would build it, instead of failing halfway through.
| Toolchain | From any machine | Needs its own system |
|---|---|---|
| Go, Bun, Deno, .NET | Windows, Linux, macOS | — |
| Rust | all three, with cargo-zigbuild | macOS apps using system frameworks |
| Electron | Linux (tar.gz) straight from Windows; Windows and Linux elsewhere with Docker or wine | macOS; AppImage and deb need Linux |
| Python | Windows, Linux, macOS, as the target’s own Python with its wheels; PyInstaller where it runs | a dependency with no wheel for that system |
The toolchain is recognised from the files in the folder. To settle it, or to build with a command of your own, describe the build in coderook.build.json, which is saved with the project; cbx build init writes one. Output lands in .coderook/build/<target>/, which is never uploaded and which git ignores.
{
"preset": "custom",
"command": "make dist TARGET={target} OUT={out}",
"targets": ["windows-x64", "linux-x64", "macos-arm64"],
"docker": { "linux-x64": "gcc:14" }
}A target that needs its own system — a Mac app, most obviously — can be built on that machine with the same command, or by a cbx runner left running there for a project’s actions.
Actions
An action is a command run on a save, on a pull request, or when somebody starts it, on CodeRook’s own runners or on your machine with cbx runner. Its result becomes a check on the pull request it ran for.
cbx runs # what has run, and how it went cbx logs 12 --follow # watch a run's output as it happens cbx cancel 12 # stop a run that is waiting or working cbx runner # run this project's work on this machine
A run can be cancelled from the Actions tab or with cbx cancel. It is recorded as cancelled at once, and a runner working on it stops the work at its next heartbeat, including anything the command started. A run nobody picks up within a day is ended and says which machine it was waiting for, so a required check never holds a pull request in silence.
CodeRook’s own runners give every account the same 1,000 minutes a month; a run past that is ended with that reason. Your own machine has no limit. The Actions tab shows the minutes used, the pass rate, the median run and what is running now.
Every run is listed on the Actions tab with filters for its result and action; a finished one can be run again (Re-run, or cbx rerun 12) against the same save, and opening a running one follows its output live. History shows a tick beside each save for how its runs went.
Whoever started a run hears when it fails, in their inbox and by email, and once more when the next run of that action passes. The email can be switched off in Settings (“A run I started fails”). The desktop app shows inbox items as system notifications while it is open.
cbx rerun 12 cbx approve 14 # a run waiting for approval cbx actions set Nightly --schedule "0 3 * * *" --timeout 30 cbx actions set Deploy --approval on --concurrency deploy --cancel-old cbx secret set DEPLOY_TOKEN # asked for, never shown
Schedules, approval, limits and secrets
Besides saves, pull requests and pressing Run, an action can start when an issue is opened, labelled or commented on, when a release is published, when a pull request lands, or on a schedule (five-part cron in UTC; the schedule is checked every five minutes). One marked as needing approval waits for somebody who can change the project; one with a time limit is stopped and marked failed when it runs over; actions in a concurrency group run one at a time, and a new run can cancel the group’s older ones.
Secrets (Settings, or cbx secret) reach runs as environment variables of the same name, but only runs started by somebody who can change the project, and run logs show *** in their place. Every run is also handed:
CODEROOK_TOKEN this run's own token: comment, label, close, check, release CODEROOK_API where to send it (cbx inside a run needs no setup) CODEROOK_PROJECT the project CODEROOK_RUN the run's number CODEROOK_EVENT_NAME what started it: version_saved, merge_opened, issue_opened, ... CODEROOK_EVENT the event as JSON: which issue, label, release or pull request MATRIX_<NAME> this run's matrix values
The token reaches only its project, can do only those few things, and ends with the run. What it does starts no further runs, so two actions cannot set each other off for ever. A public project can show a badge in its README: https://api.coderook.com/v1/public/projects/owner/project/badge.svg (add ?action=Build for one action).
Workflow files
Actions can also be written as YAML files in .rook/workflows, versioned and reviewed with the code they build. They take effect when saved to the default line; a file with a mistake changes nothing and its saver is told what is wrong. Each job becomes an action of its own. Check them before saving with cbx actions check.
name: Build
on:
save: { lines: [main], paths: ["src/**"] }
pull_request: { into: [main] }
schedule: "0 3 * * *"
concurrency: { group: build, cancel-in-progress: true }
jobs:
test:
runs-on: hosted
timeout-minutes: 20
matrix: { node: [18, 20] }
steps:
- run: npm ci
- run: npm test
package:
needs: [test]
approval: true
artifacts: ["dist/*.zip"]
attach: true
steps:
- run: npm run packageEvents: save, pull_request, merged, issue (with types: [opened, labeled, commented] and labels), release, schedule and manual. Steps run in order and stop at the first that fails; needs waits for other jobs to pass and is skipped if one fails; a matrix makes one run per combination. Runs set off by people outside the project (an issue on a public project, a pull request from a fork) act as the workflow’s owner, are capped per hour, and get no secrets.
Game and software publishing
Open Repository Settings → Publishing and choose Create creator page. Confirm the repository to link it; the page starts unlisted. Once linked, its release dialogs offer Assign release files. Attach packaged builds to a completed version, choose their platforms and architectures, then review and publish. Only selected attachments are distributed through the published page; repository access is separate.
Choose Games, Software, Mods or Assets and whether the published page appears in Explore. An unlisted page is reachable through its address; unlisted does not verify a player’s identity.
- Public: anybody can download the selected build.
- Link holders: a revocable, expiring channel key unlocks the build. Whoever holds the key can use it.
- Invited testers: named CodeRook users sign in to download without becoming repository collaborators.
- Patreon supporters: connect the creator’s campaign, select eligible paid tiers, and have players link Patreon. Membership is verified for that specific creator when a new download starts. Patreon linking is available once CodeRook’s developer app is configured.
Publishing does not change who can read the source. Protected builds require private versions that do not already expose public downloads. A file must have a clean malware-scan verdict before publishing and downloading. Withdrawing a build invalidates its download grants.
Direct links
Links can select an exact version or follow the newest version on a release channel, for a platform and architecture. Authorised players download in their browser. Players who need to sign in or verify support are returned to the download. A shared channel key is kept in the link’s fragment and sent in the verification request, not placed in updater source code.
Update checks for protected builds
The existing public Electron feeds remain public-only. Apps can use the publishing JSON API for selected build files and protected releases. Use CodeRook OAuth with PKCE and the downloads:read scope for a player-authorised updater; this scope does not grant repository access. Link the player’s Patreon account through CodeRook before checking supporter builds.
POST /v1/account/publication-downloads
Authorization: Bearer <player-authorised token>
Content-Type: application/json
{
"owner": "creator",
"slug": "my-game",
"channel": "stable",
"version": "latest",
"platform": "windows",
"architecture": "x64",
"intent": "check",
"installedVersion": "1.2.0"
}A check returns the eligible version, whether it is newer, its size and SHA-256, without creating a transfer grant. Request intent: "download" to receive a temporary URL for those build bytes. Native clients use the response’s absolute apiUrl; the relative url is for CodeRook’s browser proxy. Send the player’s authorisation on protected transfer requests as well. Never embed creator credentials in a game or forward them to players.
Guest public and shared-link requests use POST /v1/publications/downloads with the same payload; shared links additionally supply their key. Resumed requests stay on the same immutable file, even when a newer build is published. Compressed stored objects can require decoding again to satisfy a range; incremental uploads do not imply differential game patches.
Updating an app from CodeRook
If your project is public, its newest release can be the place your app checks for updates. Release a version named like a version number and attach the installer to it — nothing else needs setting up.
cbx release 1.4.0 # name the newest save cbx attach ./dist/MyApp-Setup-1.4.0.exe # the installer people get cbx release --downloads public # only if the source stays private
An Electron app built with electron-builder can then use its ordinary auto-updater. Point the generic provider at the project’s update feed:
"publish": [{
"provider": "generic",
"url": "https://api.coderook.com/v1/public/projects/you/my-app/updates"
}]The feed answers latest.yml, latest-mac.yml and latest-linux.yml with the release’s version, every attached file, and each file’s size and SHA-512, which the updater checks before it installs anything. A release named anything other than a version number — 1.4.0 or v1.4.0 — is refused, because an updater has nothing to compare it with. Opening the feed’s own address, without a file name, says which of the three the newest release can serve and, when it cannot, why. Claude can check the same thing for you through the CodeRook connector: ask whether a project is ready to update its app.
Anything else can ask for the same answer as JSON, in one request:
GET /v1/public/projects/you/my-app/releases/latest
{
"version": { "name": "1.4.0", "semver": "1.4.0", "notes": "...", "releasedAt": "..." },
"files": [{ "name": "MyApp-Setup-1.4.0.exe", "sizeBytes": 95012345,
"sha256": "...", "url": "https://api.coderook.com/..." }],
"updateFeed": "https://api.coderook.com/v1/public/projects/you/my-app/updates"
}The code can stay private in the same project. Updaters need no account, so the project they read has to be public, but its releases can offer their downloads on their own (Releasing downloads without the source): the feed and the installers are served, and the source is not.
Pages: a project as a website
A public project can be served as a website: a Unity or Godot game playable in the browser, a React app, a plain HTML page. CodeRook serves the files of a published version as they are — nothing is built on our side, so what you save is what people get.
Pages is built and is coming once its domain is live. Until then the settings can be set and saved, and the address reads Goes live when the Pages domain is set up.
- Save the project, including the build you want served (see below about
dist/andbuild/). - Publish a version: Make public on that save in the project’s History tab, or
cbx release. - Make the project public, under Who can reach it in its Settings.
- In Settings → Pages, press Set it up for me. It looks at the published files, picks the folder and the options, and turns Pages on — and says what it chose and why.
Only the project’s owner or an admin can change these settings; anybody else on the project can see them.
What it recognises
- Unity WebGL builds, including ones built with Brotli or gzip compression.
- Godot web exports, with cross-origin isolation switched on, which Godot 4 needs for threads.
- Vite, React and Next.js static builds — the output folder, not the source.
- Plain HTML: any folder with an
index.htmlin it.
If it finds nothing it says why rather than guessing. You can always choose the folder yourself instead: type it under Or say it yourself and press Save and turn on.
Build folders are left out by default
dist/, build/ and similar are build output, so the usual ignore rules keep them out of a save — which is right for the source, and exactly the folder Pages needs to serve. The build has to be in the published version for it to be served. cbx pages auto notices when that is the problem and offers to include the folder; on the website, Set it up for me tells you when this is why it found nothing. Or do it yourself: take the dist/ line out of .gitignore, or add a ! rule below it, then save and publish again.
# .gitignore dist/ # ...but send the build, so Pages can serve it !dist/
What gets served, and where
Only published versions are served — the newest one, unless you choose otherwise. A save you have not published is never on the site, so you can keep working without changing what visitors see. Making the project private, or turning Pages off, takes the site down.
Each project gets its own address, https://<project>--<owner>.<domain>/, all in lower case. The domain is coming soon. Because every project has its own address, one project’s site cannot read another’s storage or your CodeRook sign-in. The project name and your username together, with the -- between them, have to fit in 63 characters.
Two options
- Single-page app. An address that is not a file gets
index.html, so an app that does its own routing — React Router, Vue Router — works when somebody opens a link straight to/settings. - Godot / multi-threaded (cross-origin isolation). Sends the headers a page needs to use threads and
SharedArrayBuffer, which Godot 4 web exports built with threads require. Anything the page loads from another site then has to allow it, so leave it off unless the build asks for it.
From the command line
cbx pages # is it on, where, what is served cbx pages auto # the same as Set it up for me cbx pages on --folder dist [--spa] [--isolate] cbx pages off
What is not allowed
A site is part of the project, so the acceptable use policy applies to it the same way: no malware, no phishing or fake sign-in pages, no pages built to pass as another person or organisation, nothing unlawful. A site that breaks it is taken down with the project. To report one, use Report on the project’s page, or write to security@coderook.com with the link.
Once a project has a live site, its public page carries a Play button beside Download project that opens it in a new tab.
Reading a public project
Everything a visitor can see on a public project can also be read without signing in, from https://api.coderook.com/v1/public. A private project answers as if it did not exist. Where a route takes a version, latest means the newest one on offer.
GET /projects?q= public projects, newest first
GET /projects/{owner}/{slug} one project
GET /projects/{owner}/{slug}/versions the versions it offers
GET /projects/{owner}/{slug}/releases every release, with its downloads
GET /projects/{owner}/{slug}/releases/latest newest release and its files
GET /projects/{owner}/{slug}/updates which update feeds it can serve
GET /projects/{owner}/{slug}/updates/latest.yml an update feed (see above)
GET /projects/{owner}/{slug}/activity what has happened lately
GET /projects/{owner}/{slug}/versions/{version}/files every file, with digests
GET /projects/{owner}/{slug}/versions/{version}/file?path= one file
GET /projects/{owner}/{slug}/versions/{version}/readme its README
GET /projects/{owner}/{slug}/versions/{version}/last-changes?path= which version last changed each entry
GET /projects/{owner}/{slug}/versions/{version}/archive the whole version as a zip
GET /projects/{owner}/{slug}/versions/{version}/attachments files attached to it
GET /projects/{owner}/{slug}/versions/{version}/attachments/{id} download one
GET /profiles/{username} a public profileA release whose downloads are public on their own appears in releases with sourcePublic: false; its attachments download and its update feed is served, while the version routes answer as if it did not exist. Every project page on the site also lists its own addresses under Use this project from code on the Releases tab.
The same routes are described for tools at api.coderook.com/openapi.json, and api.coderook.com itself says what the API offers and where each part is explained.
What the project’s owner has said about machines applies here too. A project that has asked not to be downloaded by automated tools refuses those requests, and says so in the response rather than sending an empty file.
Mod repositories for games
A mod repository is where a game’s players get mods from. Whoever makes the game sets one up and decides its rules; modders publish to it with their own CodeRook accounts; the game reads a catalogue signed with the repository’s own key and downloads what the player picks. Nothing is listed until it has passed the rules and a malware scan.
cbx mods create my-game --name "My Game" --allow .png,.ogg,.json --nested .pak cbx mods builds my-game --new # a build secret for the game (shown once) cbx mods info my-game # the address and public key to pin
create prints the repository’s address, https://mods.coderook.com/v1/modrepo/my-game, and its public key. Put both in the game. An account can run five repositories, and cbx mods delete removes one with everything in it.
The rules
Every part of a mod is a zip. Each one is opened and every file in it checked before it is listed:
- File types.
--allowlists the extensions a mod may hold; anything else is refused.--blockrefuses more. Programs and scripts (.exe,.dll,.bat,.ps1,.jsand the rest) are refused in every repository, and so is a file that is really a program whatever its name says. - Archives inside.
--nestednames archive types the game reads (a.pak, a.ztd); those are opened and held to the same rules. - The manifest. Each part carries a manifest,
mod.jsonunless--manifestsays otherwise (nonefor no manifest), and the fields named in--fieldsmust match the mod’s entry. - Size.
--max-part,--max-files,--max-parts, and--max-ratiofor how many times its size a part may unpack to. - Who uploads.
--uploads anyone(the default) orowner. - Malware. Every file is scanned with ClamAV, with signatures refreshed through the day.
Change the rules later with cbx mods settings; --disable stops serving and taking uploads without deleting anything.
Publishing a mod
cbx mods publish ./BetterTrees --repo my-game cbx mods publish ./HD-Art --repo my-game --part animals --version 2.1 cbx mods list my-game cbx mods remove better-trees --repo my-game
publish reads the folder’s manifest for the mod’s id, name, folder and version (the flags override it), zips the folder, uploads it in pieces that resume, and waits for the checks. A rejected part says what was wrong, file by file. A mod id belongs to whoever created it; a part being checked cannot be replaced until the result is in.
What the game does
The game proves it is an official build of itself by signing each request with its build secret. Five headers go with every request (the X-ZT1- names work too, for ZT1):
X-Mod-Build the build id: first 8 hex of SHA-256(secret) X-Mod-Install 32 hex, random per installation, kept X-Mod-Time Unix seconds; refused more than 5 minutes off X-Mod-Nonce 32 hex, new for every request X-Mod-Sig hex HMAC-SHA-256(secret, canonical) canonical = METHOD \n path?query \n build \n install \n time \n nonce \n hex SHA-256(body)
Any failed check is a bare 401. The signed routes:
GET /v1/modrepo/{repo}/mods {"catalogue": "<JSON text>", "signature": "<base64>"}
POST /v1/modrepo/{repo}/mods/{mod}/parts/{part}/download {url, expires, size, sha256}
POST /v1/modrepo/{repo}/mods/{mod}/report {"reason": "malware|broken|copyright|offensive|other", "detail"?, "part"?}The signature is ECDSA P-256 over SHA-256 of the catalogue text exactly as sent, 64 bytes (r then s) in base64. Check it against the pinned public key (X then Y, 128 hex) before parsing the catalogue; it lists each mod’s id, name, author, folder, version and parts, each with its size, SHA-256 and file count. A download link lasts five minutes, needs no headers, answers Range so an interrupted download resumes, and the game should check the SHA-256 before installing.
Running one
cbx mods builds my-game --register build-secret.bin # your own secret, never printed cbx mods builds my-game --off 1a2b3c4d # an old build stops working cbx mods reports my-game # what players reported cbx mods block someone --repo my-game --take-down # stop an uploader, hide their mods cbx mods list my-game --all
Everything the command line does is plain HTTPS with a CodeRook token: /v1/modrepo/{repo}/my/mods for uploaders, /v1/modrepo/{repo}/admin/… for the owner (settings, builds, installs, uploaders, reports, checks), and GET /v1/modrepo/{repo}/info for anybody.
If you already use git
Installing the command line also installs a git remote helper, so git talks to CodeRook directly — both ways. Nothing here is a CodeRook command wearing git’s clothes: the operations are the same ones cbx performs, reached from git.
git clone coderook://my-project # the remote is called origin git remote add coderook coderook://my-project # or add it to a repository you have git push coderook main git fetch coderook
Each commit becomes a version, oldest first, keeping its message. Later pushes send only what is new. Branches become lines, and a branch pushed for the first time starts from the commit it forked at rather than from wherever the project happens to be. Tags become releases — a release is a version with a name on it, which is what a tag is too.
Cloning reverses it, and works on any project — including one that has never been near git. A project built entirely from the desktop app clones into an ordinary git history, with the messages, dates and authors it was saved with.
If your folder is a git repository, use this rather than cbx submit: each commit arrives with its own message, and every editor’s built-in git panel works with CodeRook without a plugin. cbx submit is for folders that are not git repositories. The helper comes with npm install --global @coderook/cli; the Claude Code plugin and npx do not put it on your PATH.
A project you started with cbx submit
Its saves did not come from git, so the first push from your repository is refused rather than replaying your history on top of them. If the repository holds that project, adopt it once with git push -o adopt coderook main. The project’s saves stay as they are, your current commit is saved on top as one save, and every push after it sends one save per commit. Or start from the project: git clone coderook://my-project and carry on in the clone.
The same operation, said either way
Eleven of the commands have a git equivalent. These are the same operation reached from two places, not two things that resemble each other.
| CodeRook | git |
|---|---|
cbx submit | git push coderook main |
cbx get | git pull |
cbx clone ledger | git clone coderook://ledger |
cbx status | git status |
cbx versions | git log |
cbx tracks | git branch |
cbx track main | git checkout main |
cbx promote 41 --name v1.0 | git tag -a v1.0 |
cbx releases | git tag -l |
cbx notes 41 | git notes --ref=coderook show |
cbx notes 41 --edit | git notes --ref=coderook edit |
What has no git equivalent
Most of the command line, and for two different reasons worth keeping apart.
Git never had the idea. Issues, labels, collaborators, watching, tokens, automations and their runs, projects themselves. These are the things a forge grew around git rather than anything git does — on GitHub you would reach for gh, not git. Git is a version control tool; deciding who may read something is a different product.
CodeRook has an idea git does not. This set is the more interesting one. cbx mark hides, pins or labels a single save, and git has no per-commit state at all beyond the commit. cbx propose offers a line to main to be checked and reviewed, which on GitHub is a pull request and in git is nothing; cbx held and cbx review are a single save waiting on somebody’s approval, which neither has. cbx take-down removes a published version’s files while its row stays; git cannot do that without rewriting history and breaking every clone. And cbx undo looks like git reset but is not — reset is a local opinion, and this is a published statement about where the project stands.
So git covers moving files and naming versions. Everything about deciding — what is offered, what is held back, who can see it, what is wrong with it — is CodeRook’s, and that is most of what the command line is for.
What it will not do, said here rather than left to be discovered: a push that is not a fast-forward is refused before anything is uploaded, so you pull and merge in git as you would anywhere; force pushes and branch deletion are refused, because a published version cannot be unpublished; a push straight onto a protected line is refused like any other direct save, so push a branch and propose it; and submodules are skipped and named. The executable bit travels both ways. A round trip returns the same files, not the same commit ids — a commit id covers its author and timestamps, and CodeRook records who published a version rather than the original stamps.
AI assistants: choose a setup
Use the remote connector to work on projects stored on CodeRook, or the CLI skill to work with a folder on your computer. A plugin bundles instructions with its connection; a skill on its own teaches the assistant a workflow.
| You want to | Use | Setup |
|---|---|---|
| Read, edit and save a remote project from Claude Code | Connector and bundled skill | Claude Code plugin |
| Save or fetch a folder on this computer with Claude Code | CodeRook CLI and local skill | CLI skill |
| Work on remote CodeRook projects from Codex | Connector and bundled skill | Codex plugin |
Each person connects their own CodeRook account. Installing a plugin does not copy another person's projects, credentials or workspaces. Your account permissions and each project's machine-reading setting still apply.
Claude Code connector
The remote plugin includes the CodeRook connector and its skill. It can browse saved versions and work on private remote drafts without downloading the whole project. Run these commands inside Claude Code:
/plugin marketplace add AlexC1991/coderook-claude-plugin /plugin install coderook@coderook-claude
- Review the plugin and choose the installation scope offered by Claude Code.
- Run
/mcp, select the CodeRook connection and follow its sign-in prompts. - Sign in on coderook.com, review the requested permissions and select Allow access.
- Start a new session if prompted, then ask Claude to list your CodeRook projects.
No CodeRook CLI install or personal access token is needed for this remote connection. The plugin repository contains the published package and its setup instructions.
Connect directly without the plugin
To add just the remote tools, run this in your terminal, then use /mcp inside Claude Code to sign in:
claude mcp add --transport http --scope user coderook https://api.coderook.com/mcp
Choose the plugin or the direct connection; adding both is not necessary. The plugin also supplies the workflow skill. See Claude Code's MCP guidefor connection and authentication troubleshooting.
Claude Code CLI skill
Use this route when the project's files are on your computer and you want Claude Code to run cbx commands for you. Run these in a terminal:
npm install --global @coderook/cli@latest cbx sign-in cbx skill
On Windows, if PowerShell blocks the script launchers, use npm.cmd and cbx.cmd instead. You do not need to change PowerShell's execution policy.
cbx skill writes ~/.claude/skills/coderook/SKILL.md for all your projects on that machine. Use cbx skill --project to write .claude/skills/coderook/SKILL.md in the current project instead. Run it again after updating the CLI to refresh the installed skill.
Inside Claude Code, invoke /coderook or ask in ordinary words: show my changes, save this folder to CodeRook, or fetch the latest version. The skill uses your local files and CLI sign-in; it does not create a remote connector session. Check that sign-in with cbx whoami.
If you already installed the remote plugin, add the CLI skill only when you also need to work with local folders. The two routes serve different locations for your project files.
Codex plugin
The CodeRook Codex plugin bundles the remote connector and the CodeRook workspace skill. Install it from CodeRook's repository marketplace. Run these in a terminal with Codex CLI installed:
codex plugin marketplace add AlexC1991/coderook-claude-plugin codex plugin add coderook-codex@coderook
The repository name is shared with the Claude plugin, but this command selects the separate coderook-codex package. If codex is not found, install the Codex CLI or put its executable on your PATH before running terminal commands.
Complete the CodeRook account connection when prompted. Review access on coderook.com, then start a new Codex session so the plugin's tools and skill are available. In clients that expose repository marketplaces, you can also browse the CodeRook source in the plugin browser. Availability depends on your client and workspace settings; this repository install does not require a listing in the reviewed public directory.
Ask: List my CodeRook projects, or Open a workspace for my project, show me the current UI, and prepare a change for review. You do not need the CodeRook CLI to use the remote plugin. See OpenAI's plugin guide for client setup, and the CodeRook plugin repository for package instructions.
Remote connector and workspaces
Claude Code, Codex and compatible remote MCP clients connect to the same CodeRook service:
https://api.coderook.com/mcp
In Claude on the web or Desktop, add this URL through the custom connector settings and follow the browser sign-in. The approval screen explains the access requested. Use your own account and review those permissions before allowing the connection. Manage or revoke it from your CodeRook account's access settings.
What you can do
- List projects, browse saved files and compare versions.
- Open a private draft, read and search its files, make edits and review the diff.
- Save the reviewed draft as a complete CodeRook version.
- Run configured checks and builds; deploy a saved version to a configured target when you request it.
- Manage names, releases, visibility and undo within the work you authorize.
Workspace editing, jobs and deployment require their respective permissions. If an older connection only shows reading tools, update the plugin and reconnect to review the additional grants. A skill alone does not add permissions. Taking down a version's files or opening a project to others requires explicit authorization.
Continue on another computer
Ask the assistant to leave a handover note with the workspace ID, current revision, completed work and next step. Connect the same authorized CodeRook account on the other computer and resume that workspace. This carries project state and the note, rather than either assistant's chat history. Separate drafts can be used for independent work.
Hosted checks can run while your desktop is off. Native Windows builds and app or game tests need a compatible connected runner; previews and deployments need project-configured recipes and targets. A saved version or successful build does not deploy a website by itself.
Hand a draft to a machine that is not signed in
In a project's Actions → Workspaces, choose Assign to a PC on a draft, or run cbx workspace assign <project> <draft>. CodeRook makes a token that reaches that one draft, shown once. With it, the other machine can read the project, change and save that draft and run its configured checks. It cannot deploy, change settings, open your other drafts or hand the draft on. Revoke it any time; archiving the draft ends every token.
npm install -g @coderook/cli cbx sign-in # paste the token cbx workspace checkout <project> <draft> # the draft as files cbx workspace push --save -m "What changed" # send edits, then save
A handed-over machine never saves straight onto your line. Its saves go to the draft's own line, drafts/<draft-name>-<id>, which only the project's people can see, and open a pull request into the draft's line. You, or whoever you ask to review it, approve and merge it there, with the line's merge rules, required checks and code owners applying as usual; set it to merge by itself once those pass if you like. Saves made signed in as you go straight to the line, as before.
A machine already signed in as you needs no token: it opens its own drafts. Edits go in as text, each checked against the copy the machine last had, so a clash is refused rather than overwritten, and the save itself goes through the ordinary rules: merge tracks, protected lines and every upload check.
See which machines are working on a project
Actions → Linked folders, or cbx machines, lists each PC with a folder linked to the project, and each machine a draft was handed to: the version it holds, how far behind the line it is, and how many files it has not saved. A machine appears when cbx runs there (status, get, submit, and the workspace commands); only the folder's name is sent, never its path. Set CODEROOK_NO_PRESENCE=1 to keep a machine off the list. Runners stay under Actions → Runners.
Optional local MCP server
The CLI's original cbx mcp mode is a separate, local read-only setup. For remote workspace tools through the CLI, use cbx mcp --workspaces with an appropriately authorized CodeRook sign-in. The remote plugins above connect directly and do not need this extra server.
Bundles
A .cbx bundle is a whole project as a single file: every version, every file, deduplicated and compressed. It is how you move a project onto a machine that is not online, hand it to somebody, or keep a copy somewhere that is not us.
cbx bundle . my-project.cbx cbx inspect my-project.cbx # what is inside, without unpacking cbx unbundle my-project.cbx ./restored