Your repository history, encrypted before it leaves the machine.
GitVault is a Git remote whose contents are encrypted on your own machine and stored as a chain
of signed, admitted heads. It exists so that your repository history outlives the machine it was
written on — and so that making it outlive that machine never required handing Run402 the
plaintext. The vault protects source history from host-side loss while a principal
keystore survives, so back up ~/.config/run402/gitvault and read
If you lose the keystore before you rely on any of this. The wire protocol
is r402s/v0.
These three sentences are the entire approved claims vocabulary for this product. They are three separate promises with three separate strengths, and conflating them is exactly the mistake this page exists to prevent.
The first claim is narrower than “we hold none of your code,” and the narrowness is deliberate. Run402 stores two different things about your project, under two different trust positions, and blurring them would be the overclaim.
| Lane | What it holds | Who can read it |
|---|---|---|
Vault lane (r402s/v0) |
Encrypted pack sets, signed create-only heads, ref transactions, checkpoint sets, envelopes | Holders of the repository key only — today, your own keystore |
| Deploy lane (CAS) | The plaintext source-artifacts of every deploy — what you chose to publish | The platform, custodially |
In the vault lane, source payload and repository-history content are ciphertext-only; the substrate retains only enumerated plaintext metadata and holds zero vault keys. The deploy lane is not a leak in the vault's design — it is the disclosed custody boundary, and it is also the only restore path that survives losing your keystore entirely. The platform can read what you deployed. It cannot read what you did not.
Three commands, run from inside your git working tree. The first push allocates the vault, prints the recovery receipt once, and admits generation 1.
# 1. Provision the project. Inside a repository that already exists, this adds a
# `run402` remote (run402::<org_id>/<project_id>) by default.
# Not a repository yet? `run402 init --git-remote` creates one first.
run402 init
# 2. Push. Capture the working tree, encrypt it, publish a signed head.
run402 gitvault push --message "wip: refactor the parser"
# ...or push with git itself, through the git-remote-run402 helper:
git push run402 main
# 3. See what this machine and the control plane each believe about the vault.
run402 gitvault status
# 4. Verify the head chain from your authenticated pin up to the newest listed
# generation. Fails closed on a regression, a gap, or a descriptor this
# client cannot validate. The verified prefix persists, so a budget-exceeded
# run resumes where it stopped.
run402 gitvault verify --budget 500
Before push reports that anything landed, the client compares every finalization
receipt against its local expected manifest and reads the admitted head back from storage. A
200 alone is never enough — a client that trusts the status code has handed
the host a freshness oracle it was not supposed to have.
# Publish a checkpoint covering the canonical refs, every root unexpired at the
# cutoff, and the HEAD target, under a maintenance lease (owner).
run402 gitvault compact
# Report which retention roots have passed their retention window.
# DRY RUN in V0: nothing is submitted and no bytes are deleted.
run402 gitvault prune
Everything the CLI can do is reachable programmatically with identical semantics — the CLI
is a thin adapter over r.gitvault, not a second implementation. Vault reads run
anywhere; the verbs that touch a git working tree or the on-disk keystore are Node-only.
import { run402 } from "@run402/sdk/node";
const r = run402();
// Read side: the vault record — policy, allocation, chain tip, storage state.
const vault = await r.gitvault.forProject("prj_...");
// Write side: capture and publish, then verify what was admitted.
const pushed = await r.gitvault.push({ project_id: "prj_...", message: "wip" });
const state = await r.gitvault.verify({ project_id: "prj_..." });
The gateway surface is live at /gitvault/v1 and documented in
/llms-full.txt and /openapi.json. Run
npx run402 gitvault --help to see what your installed client carries.
r402s-verify is an independent-lineage verifier for the same protocol: a separate
language, a separate authorship, and a separate primitive stack from the SDK, deliberately
sharing no implementation code with it. That non-sharing is the point — a
differential verifier that reuses the code it is checking verifies nothing.
# Build the verifier (Rust).
cargo build --release # binary at target/release/r402s-verify
# Verify a vault's head chain from a bucket export, against your own pin.
r402s-verify --schemas ./schemas chain ./export --pin-generation 000000000000002a
# Replay the protocol's published test vectors and print a per-class tally.
r402s-verify vectors ./vectors/vectors.json
You do not have to deploy anything. A vault-only project — run402 init, then
git push run402 …, then compact / prune / verify, and never a deploy — is
a supported shape, not a degraded one. Nothing in allocation, admission, retention, or
maintenance requires a deployment, an apply operation, or a release to exist.
One consequence is worth stating plainly: a vault-only project has no deploy lane. The disclosed plaintext custody boundary is then empty — there are no deployed artifacts for the platform to hold — and there is consequently no custodial restore path if your keystore is gone. The purest form of the product is also the one where the keystore qualifier below bites hardest.
There is no separate GitVault price. A vault's bytes count against the same organization-pooled storage budget your projects already share — the figure quoted on each tier card at /pricing — charged once per unique object, with a 64 KiB per-object accounting floor. An upload session reserves its bytes at issuance and releases them if it aborts or expires; a maintenance lease adds bounded headroom so a compaction can temporarily exceed the steady-state limit. Over the pool, the write is refused rather than billed.
Tiers themselves are prepaid leases paid by the agent with USDC on Base via x402 or pathUSD on Tempo via MPP, or with credits bought by a human on a credit card via Stripe.
Content is hidden; structure is not. This is not zero-knowledge hosting, and the honest way to say so is to enumerate the boundary rather than gesture at it.
| Observer | Sees | Does not see |
|---|---|---|
| Run402 (control plane) | That a vault exists and whose it is; principal identities and access times; object counts, ciphertext sizes and ids; generations and cadence; admission times; capture bindings; policy state; the structural skeleton of maintenance. Plus, in the deploy lane, the plaintext artifacts of every deploy. | Vault content of any kind. The repository key. Branch names, commit messages, file paths, commit ids. Any un-keyed digest of plaintext. |
| Bucket operator | The same object set minus API context, plus access patterns — which keys, when, from where. | Content; and the control-plane context the row above has. |
| Network | Endpoints, sizes, timing. TLS covers the rest. | Object ids, content, identities. |
| A deploy delegate | Heads, ciphertext objects, receipts for what it uploads. | Content. And it can never compact, prune, repair, change the policy, or override activation — those are owner capabilities, and a delegate is structurally never an owner. Nothing the agent holds can destroy the history it wrote. |
A second person in your organization is in the same position today: they can administer the vault's ciphertext without being able to read it, because the only envelope that exists is the creator's. That is an incompleteness we are working on, not a security feature — envelopes addressed to invited humans are a later change.
A product whose confidentiality rests on the platform holding zero vault keys has, by construction, no platform-side rescue for a lost keystore. So the durability sentence carries its qualifier in the sentence itself, never as a footnote:
The vault protects source history from host-side loss while a principal keystore survives.
The “while” clause is load-bearing. What survives what:
| You lost | Vault history is |
|---|---|
| Your repository key file, identity intact | Recovered — open your own envelope |
| Your signing key, repository key held | Readable, read-only — you can decrypt and verify, not sign |
| Your trust pin only | Recovered — re-verify from genesis against the recovery receipt |
| The whole keystore, every copy | Terminal |
| The whole machine, keystore not backed up | Terminal |
The client says this to your face rather than burying it. run402 gitvault status
prints, verbatim:
whole-machine or whole-keystore loss is terminal for vault history
until human envelopes ship
and the error registry entries are equally blunt about what to do next:
VAULT_UNRECOVERABLE (retryable: false)
next_action: no principal keystore survives; restore deployed artifacts from CAS
KEYSTORE_MISSING (retryable: false)
next_action: restore ~/.config/run402/gitvault from backup or accept vault loss
Two practical consequences. Back up your keystore. And the
recovery receipt that init prints is an integrity anchor, not a decryption
key: it proves the vault you are served is the one you created, and it can decrypt nothing. It is
not a secret — print it, mail it to yourself, put it in the README. The more copies the
better.
Unreachable history — dropped or force-displaced tips — is recoverable for at least 90 days, measured from the moment the drop was committed to storage rather than from when the platform first heard about it, so a delayed record can extend the lane but never shorten it. Expiry is schedule-relative, not calendar-exact; deletion is two-phase and attested; every delete set is receipted; a prune intent is a stored, signed object you can audit.
Against an honest host, that is a strong construction. Against a dishonest one it binds nothing, and we will not pretend otherwise: retention is an operational promise of the platform, not a cryptographic guarantee against it. There is no cryptography by which a storage provider can be prevented from deleting what it stores. What the protocol does instead is make every deletion attributable — a prune that was not owner-authorized leaves a ledger gap your own client detects. Silent loss becomes loud loss.
Availability is operational in the same way. If the vault service is unreachable, a push-gated deploy is blocked rather than silently activated without a capture, and the owner's audited override is the documented escape. An outage is a deploy blocker by design, not a data-loss event.
The vault routes, capabilities, and error codes are in
/llms-full.txt and /openapi.json under
/gitvault/v1. Read-side vault routes are never lifecycle-gated — a vault stays
readable while its organization is in billing grace. Unauthorized, nonexistent, and wrong-project
all return one identical refusal, so no request reveals whether a vault exists.