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

  1. Create an account, or sign in with Google or GitHub.
  2. 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 is cbx.
  3. Point it at a folder — open it in the application, or run cbx sign-in and then cbx status inside it. Either way you are shown what would be sent before anything leaves the machine.
  4. Settle the rules once: what belongs in the project and what does not. cbx ignore --suggest names the dependency folders, virtual environments and build output it can see, and what each is costing you.
  5. 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 issue

Issue 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.

ToolchainFrom any machineNeeds its own system
Go, Bun, Deno, .NETWindows, Linux, macOS—
Rustall three, with cargo-zigbuildmacOS apps using system frameworks
ElectronLinux (tar.gz) straight from Windows; Windows and Linux elsewhere with Docker or winemacOS; AppImage and deb need Linux
PythonWindows, Linux, macOS, as the target’s own Python with its wheels; PyInstaller where it runsa 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 package

Events: 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.

  1. Save the project, including the build you want served (see below about dist/ and build/).
  2. Publish a version: Make public on that save in the project’s History tab, or cbx release.
  3. Make the project public, under Who can reach it in its Settings.
  4. 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.html in 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 profile

A 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. --allow lists the extensions a mod may hold; anything else is refused. --block refuses more. Programs and scripts (.exe, .dll, .bat, .ps1, .js and the rest) are refused in every repository, and so is a file that is really a program whatever its name says.
  • Archives inside. --nested names 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.json unless --manifest says otherwise (none for no manifest), and the fields named in --fields must match the mod’s entry.
  • Size. --max-part, --max-files, --max-parts, and --max-ratio for how many times its size a part may unpack to.
  • Who uploads. --uploads anyone (the default) or owner.
  • 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.

CodeRookgit
cbx submitgit push coderook main
cbx getgit pull
cbx clone ledgergit clone coderook://ledger
cbx statusgit status
cbx versionsgit log
cbx tracksgit branch
cbx track maingit checkout main
cbx promote 41 --name v1.0git tag -a v1.0
cbx releasesgit tag -l
cbx notes 41git notes --ref=coderook show
cbx notes 41 --editgit 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 toUseSetup
Read, edit and save a remote project from Claude CodeConnector and bundled skillClaude Code plugin
Save or fetch a folder on this computer with Claude CodeCodeRook CLI and local skillCLI skill
Work on remote CodeRook projects from CodexConnector and bundled skillCodex 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
  1. Review the plugin and choose the installation scope offered by Claude Code.
  2. Run /mcp, select the CodeRook connection and follow its sign-in prompts.
  3. Sign in on coderook.com, review the requested permissions and select Allow access.
  4. 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