SECURITY

CodeRook security

CodeRook separates public repository reads, user sessions, desktop credentials, administrative bootstrap authority, metadata, and immutable object storage.

Authentication

  • HttpOnly, Secure, SameSite web sessions with double-submit CSRF
  • Revocable desktop and CLI bearer credentials stored by the operating system
  • One-time PKCE OAuth state and one-time desktop exchange codes
  • Throttled password login and one-time password recovery links

Project data

PlanetScale stores account, permission, repository, version, quota, and audit metadata. Cloudflare R2 stores content-addressed objects. Private projects return crawler-safe not-found responses and generic link cards.

How secure this is, honestly

Nothing stored on any service is completely secure, including this one, and we would rather say so than imply otherwise. What we can do is give you enough control to decide how exposed your work is, and be straight about where the edges are.

You choose whether a project is private or public. You choose whether automated clients may read it, contribute to it, download it, or call it. Private projects return the same not-found response to a stranger as a project that never existed, so their names are not discoverable. Versions are immutable once written, so a change is always an addition you can see rather than a silent edit.

What that does not do is make the service immune to compromise, protect content you have chosen to publish, or defend against someone who already has your credentials. Treat the controls as a way to set how much risk you are taking, not as a guarantee that there is none.

Specifics

  • In transit — everything is served over TLS, with strict transport security set on responses.
  • At rest — encrypted by our storage providers. Cloudflare R2 and PlanetScale both encrypt stored data by default, and we rely on that rather than adding a layer of our own. Said that way deliberately: it is a real protection, and it is theirs rather than something we built.
  • Backups — the database is dumped nightly to separate object storage and kept for thirty days, then rotated out. Backups are what a bad day is recovered from; they also mean "deleted" takes up to thirty days to become "gone", which is stated plainly in the privacy policy rather than glossed over.
  • Passwords — stored as PBKDF2-SHA256 derivations, never as recoverable text. We cannot tell you your password because we do not have it.
  • Two-factor authentication — time-based codes, with recovery codes held only as hashes.
  • Sessions — revocable individually, and revoked in bulk when an account closes. Access tokens are revoked at the same time, so a token cannot outlive the account that made it.
  • Content integrity — every stored object is addressed by a SHA-256 digest of its contents, so a file that changed in storage would no longer match the name it is stored under.
  • Immutable history — a saved version is never rewritten. Changes arrive as new versions, which means a silent edit is not a thing that can happen.
  • Private projects — answer a stranger with the same not-found response as a project that never existed, so their names cannot be discovered by guessing.
  • Automated access — per-project controls, with human verification available for owners who want it, and request budgets on the routes that are expensive to serve.
  • Deletion — closing an account puts every project on a seven-day clock, after which the stored objects are reclaimed. Deleting a single project uses a thirty-day window.

Keys and other secrets in a project

A credential that reaches a project is treated as a mistake until you say otherwise. Publishing a version that contains one is refused, by the service rather than by the app you happen to be using — so it holds for git push, the command line, the desktop app and the website equally. The refusal names the file and the line and the kind of key, and never repeats the value.

Sometimes a key belongs in a project on purpose — a test fixture, a demo token — and you can publish one deliberately. From that point the question is who gets to see it.

Who can see a stored secret

The person who owns the project, and anyone they have made an admin on it. On a team-owned project, the team's owner, admins and technicians. That is the whole list. A collaborator with write access can push to the project and still cannot read its keys, and neither can anybody browsing the site.

There is no operator override. This is decided per project, so being the owner of one project gives you nothing on anybody else's.

What everyone else gets instead

The same file, under the same name, at exactly the same size, with the secret replaced character for character. Downloads work, archives work, and the digests published in a file listing describe what will actually be served rather than what is stored.

What that looks like depends on the file. In a credential file every assigned value is covered, because there is no way to tell which of them is the secret one — the names and the comments and the shape of the file survive, the values do not:

# payments                        # payments
STRIPE_SECRET=sk_live_51NqQ4mT…   STRIPE_SECRET=*****************
PORT=8080                         PORT=****
APP_NAME=widget                   APP_NAME=******

  the owner                         everybody else

In ordinary source, only the key itself is covered and a readable prefix is left so you can tell which one it was. The code around it is untouched:

export const stripe =            export const stripe =
  new Stripe("sk_live_51NqQ…")     new Stripe("sk_live*********")
export const port = 8080         export const port = 8080

  the owner                        everybody else

Nothing announces that a file was covered. No header, no flag, no badge on the page — a marker saying "a secret was removed here" is itself the disclosure. The public is not told that a project contains one, and a stranger asking gets the same answer as they would for a project with nothing in it.

This includes you. Your own project page shows the secret covered when you are looking at the public view of it, because a screen is the easiest thing in the world to be looking over.

Getting the real file

A second button — Download with secrets — appears on the project page for the owner and the project's admins, and only when that version is actually holding something. It goes through your account rather than the public page, so what arrives is the file as you wrote it. The desktop app and the command line do the same when you are signed in as somebody entitled to it.

Pushing a covered copy back is refused. If you take a download that had the keys masked, work on it and publish, the service recognises the masked file and stops you rather than quietly writing a row of asterisks over the real value.

Where this stops

The key is not in the file we keep. When a version is published with credentials in it on purpose, what reaches storage is the covered file — asterisks where the value was — and the real values are sealed separately under a key held by the service and not by the object store. Your "download with secrets" puts them back together.

That is the difference between hiding something on the way out and not keeping it. A route added without covering, a mistake in these rules, or somebody reaching the object store itself finds files with asterisks in them.

Stated as plainly as the rest of this page: it moves the problem rather than ending it. Someone who reaches both the object store and the service's own key is back where they would have been. Two things in different places is what this kind of protection actually buys, and anyone telling you otherwise is selling something.

Two consequences worth knowing. It applies to versions published from now on — anything already stored keeps what it has until it is published again. And each sealed value is tied to the project, the version and the path it came from, so it cannot be lifted out and opened somewhere else.

Every file is opened except the ones that plainly cannot hold text — pictures, video, audio, archives, fonts, compiled binaries, documents, model weights. That is the list we keep, because it is a list that can be finished. Everything else is opened and judged by its contents rather than its name, so a key in store.custom or deploy.vars or anything else nobody thought of is covered the same as one in .env. Copies count too: .env.bak, .env.old and config.yml~ are read as what they are copies of.

There is no size at which this stops. A file is covered a window at a time rather than loaded, so a key a hundred megabytes into a file is treated exactly like one on the first line, and a file stored in pieces is put back together before it is read — a key split across two of those pieces is still found. It costs time on a download, which is a fair price for the alternative not being "we gave up because the file was big".

The check that finds keys is measured rather than asserted: against a set of twenty-two real credential formats and twenty-four things that merely look like one — commit hashes, lockfile digests, base64 images, placeholder keys — it finds every credential and flags none of the look-alikes. That number is held by a test, so a change that made it worse would fail rather than ship.

Nobody here reads your projects

No feature of CodeRook lets an operator open your private projects. There is no support tool, no admin view, and no moderation queue that reads private project contents. The administrative side of the service manages accounts, subscriptions and storage totals — it has no route that reaches a file.

That is a statement about what exists, and it was checked against the code rather than assumed. It is also why support cannot look inside a project to help you debug one: there is nothing to look with.

What you are responsible for

Your password and any access tokens you create. Keeping two-factor authentication enabled if you have it. Deciding what is appropriate to upload given that no service is completely secure. Checking that a project you make public is one you meant to make public — publishing is reversible on our side, but a copy someone already took is not.

Report a vulnerability

Send a clear description and reproduction details to security@coderook.com. Do not include live user data or destructive proof. Our vulnerability disclosure policy sets out what is in scope, what we ask while you are testing, and what you can expect back.

Where an incident is likely to cause serious harm to the people affected, Australia's Notifiable Data Breaches scheme requires notification to those individuals and to the Office of the Australian Information Commissioner. We would tell affected users directly.