← Back to run402.com

GitVault

A Git remote, encrypted before it leaves your machine.

Run402 cannot decrypt your gitvault or repository history. Deployment artifacts remain a disclosed plaintext custody boundary.

The first sentence is the entire product. You push with plain git push, and your history is encrypted on your own machine first, under a key we never receive. So there is nothing on our side to read, to index, to train a model on, to sell, or to hand to whoever asks next — not because we promise not to, but because we cannot. We hold no key to change our minds with.

Why this matters now

Readable code is usable code, by anyone who can read it. A host that can read your private repositories can — under a future policy, an acquisition, a training pipeline, a subpoena, or a breach — index it, train models on it, or hand it to someone who will. That is not an accusation about any particular host; it is what plaintext plus time does to code that sits somewhere readable. GitVault's answer is structural rather than contractual: our future policies about your code cannot matter, because we never held the plaintext to change our minds about.

The exit ramp — host-blind AND hostage-free

Host-blindness answers “can you read my code?” It does not answer “what if you disappear?” The exit ramp does. Your repository ciphertext moves directly between your machine and storage — our servers never handle it. And with the opt-in mirror, it does not even have to rest only with us. Your ciphertext replicates to a bucket you own. We can't decrypt your repository history — and we can't hold it either.

This is the recommended default, not a power-user extra. The recommended shape is three copies: your working clone, the platform’s replicated vault, and a mirror in storage you own. The door is taught in the first minute — run402 repos create prints the mirror one-liner right beside the recovery receipt — and until a first mirror write or sync succeeds, run402 doctor and repos view carry a standing vault_unmirrored finding: informational, never blocking, and computed entirely on your machine, so we never learn whether a mirror exists at all.

run402 repos mirror s3://your-own-bucket          # once; a local directory works too
run402 repos mirror --backfill                    # catch up; every snapshot dual-pushes after
run402 repos recover ./mirror --out ./restored    # the drill — no server involved

If we disappeared tomorrow, your repository is recoverable from a bucket listing and your keys, with an open-source tool. No server required. Provable, not promised. We proved it against production: a real vault, mirrored, then recovered ref-exact with the gateway deliberately unreachable. Integrity is checkable the same way. Every prune requires receipts from two independently built open-source implementations — one in TypeScript, one in Rust.

Two honest limits, stated the way we state everything: offline recovery proves validity, never freshness — a stale mirror is indistinguishable from a short history (the dual-push keeps yours current); and a mirror does not change the terminal-loss rule — without your keystore, mirrored ciphertext recovers nothing.

Bring your own bucket — the vault without a platform copy

The mirror keeps a copy in storage you own. run402 repos create --byo s3://your-bucket/prefix goes one further: the vault’s primary payload storage is your bucket. For a BYO vault, your source ciphertext never touches our infrastructure — not even encrypted. Your client writes every payload object straight to your bucket with credentials that never reach us; we keep what we have always kept — the small signed chain of heads, admissions, rotations and prunes — so admission, policy, and deploy gating work exactly as on a managed vault, and your client writes a copy of that chain into your bucket too, so the bucket alone is a complete recovery source.

Honesty follows the storage. We cannot read your bucket, so storage on a BYO vault is client-attested — the receipts say so (storage_verification: "client_attested"), never a pretend “we checked.” We can prove what should exist — the signed chain names every object a generation ever claimed — we cannot independently verify presence in a bucket we hold no credential for. And the copy count, stated plainly: run402 holds no payload copy of a BYO vault — only the small signed chain; your primary bucket is the sole copy of your source until you add a second customer-held location. A single-bucket BYO vault has fewer copies than a managed one, not more — the vault_unmirrored finding stays on, and its remedy is a second location of yours: run402 repos mirror <destination> works unchanged.

Reads survive our outage — git fetch/clone fall back to your bucket, validity, never freshness. Pushing still needs us up, in every storage model: admission is live-server, and BYO does not change that. What the platform can see is also unchanged — the observer table below applies identically. BYO moves who holds the bytes, never who can read them; it is a custody choice, not a privacy upgrade.

You do not have to move anything

Keep GitHub or GitLab as your primary — collaboration, CI, reviews, pull requests, all unchanged. Add GitVault as a second remote: a complete, continuously updated copy of your Git history that the storage provider itself cannot read. This is the pattern cold security reviews keep arriving at on their own, and it is two commands.

git remote add gitvault run402::<org_id>/<project_id>
git push gitvault --all
local repo
  → GitHub / GitLab (primary — collaboration, CI, reviews, unchanged)
  → GitVault      (encrypted second remote — the copy nobody can read)
  → your keys     (backed up independently — they ARE the vault)

Or start clean. A vault-only project — one CLI, one run402 repos create, then plain git push, and never a deploy — is a first-class shape, not a degraded one: no database, no manifest, nothing else to learn. It works the same for people and for software: a coding agent with a funded allowance can set the whole thing up end to end without a human ever opening a dashboard.

Quickstart What we claim, and how strongly What we hold in plaintext For European teams How the host side holds your bytes Keys, teammates, losing yours What GitVault is not For agents

Quickstart: create → push → fsck

Run from inside your working tree — it does not have to be a git repository yet. repos create allocates the vault immediately and prints the recovery receipt once; the first push admits generation 1. (Skipped create? A first git push against an unallocated project allocates on its own — the two never stack.)

# 1. Provision the project, allocate its vault, and scaffold the remote —
#    `origin` when free (falling back to `run402`), addressed
#    run402::<org_id>/<project_id> — or run402::<org-slug>/<name> once your
#    org has claimed a slug (`run402 org slug`). Name inferred from the
#    directory or an existing remote; a bare directory gets `git init` too.
run402 repos create

# 2. Push. Encrypted on your machine, published as a signed head.
git push origin main

#    ...or capture the working tree without git ceremony:
run402 repos snapshot --message "wip: refactor the parser"

# 3. See what this machine and the control plane each believe about the repo.
run402 repos view --human

# 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 repos fsck --budget 500

# Restore anywhere, with plain git — "anywhere" meaning any machine with a
# Run402 principal whose keystore holds an envelope for this vault. This is
# encrypted git, not a shareable link: without an envelope, a clone is
# ciphertext nobody can open.
git clone run402::<org_id>/<project_id> restored

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.

What we claim — and how strong each claim is

Every claim on this page comes from a fixed, reviewed vocabulary — the sentences above, the exit-ramp sentences, and these three. They are separate promises with separate strengths, and conflating them is exactly the mistake this page exists to prevent.

The claims
  1. Run402 cannot decrypt your gitvault or repository history. Deployment artifacts remain a disclosed plaintext custody boundary.
    Cryptographic, against Run402 itself. There is no key material server-side, so it is not a policy we follow — it is a capability we do not have.
  2. Activation requires vault admission by default; an explicit, audited override can bypass it.
    An operational platform invariant. Enforced by the platform, auditable by the owner, and bypassable only by the owner's own explicit, step-up-authorized override.
  3. Retention is an operational promise of the platform, not a cryptographic guarantee against it (the host controls timestamps and bytes).
    We commit to it; we cannot make it impossible for ourselves to break it. See Retention.

Two lanes, two trust positions

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.

LaneWhat it holdsWho 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 every key. The platform can read what you deployed. It cannot read what you did not.

Under the hood

A vault is a chain of signed, admitted heads over encrypted pack sets. Each push publishes a new generation; the gateway allocates the vault, hands out create-only presigned PUT URLs, verifies what storage actually holds, admits the head, and serves the chain back. It never sees a key. The wire protocol is r402s/v0.

Maintenance

# One verb — `git gc`'s own two halves (checkpoint publication + prune
# planning), with a stricter deletion ceremony. By default this checkpoints
# and PLANS: it walks the verified chain, computes the GC root set, and
# prints a signed intent_core. Nothing is deleted until you re-run with
# --submit --intent-core <core.json> --verifier-receipt <receipt.json>,
# carrying BOTH receipts the two-phase protocol requires — this CLI's own
# plus an independent one from r402s-verify.
run402 repos gc

From the SDK

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_...", snapshot: { 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 repos --help to see what your installed client carries. (The SDK namespace stays r.gitvault — the vault is what the thing is; a repo is what you have.)

Verify it without trusting our client

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

A vault-only project is first-class

You do not have to deploy anything. A vault-only project — run402 repos create, then git push origin …, then fsck / gc / mirror, 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 every key is gone. The purest form of the product is also the one where the key qualifier below bites hardest.

What it costs

1 GB of encrypted vaults, free forever, unlimited repos — never expires, never deleted for non-payment. The prototype tier a fresh agent lands on after its one $0.10 testnet setup payment is perpetual: no lease, no renewal clock, no billing-driven purge. GitHub is the honest comparator — free, unlimited repos, never expires — and prototype's vault floor is held to that same bar.

There is no separate GitVault price beyond that. A vault's bytes count against a SEPARATE, org-pooled vault-bytes quota — distinct from the project storage budget your projects share — billed as the larger of a 1 MB per-vault minimum or the sum of each unique object's size (with a 4 KiB per-object accounting floor), so a small vault reads as roughly the minimum rather than scaling with object count. Prototype's quota is 1 GB; hobby's is 10 GB; team's is 50 GB — each its own figure at /pricing, reported separately from project storage on GET /tiers/v1/status. 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. Vault write routes are never gated by billing state, on any tier — only the vault's own quota above bounds it, never a lease, a grace period, or an organization's lifecycle state.

Upgrading past prototype is a prepaid lease 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. If a leased tier's lease lapses, the organization's lifecycle ladder eventually returns it to perpetual prototype — nothing is deleted, capacity beyond prototype's numbers is simply frozen in place until renewal.

What the platform can see

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.

ObserverSeesDoes 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.
Someone else in your organization The vault's ciphertext and everything the control plane sees — and, once they enrol an encryption key, an envelope of their own: an enrolled member is wrapped as a recipient and can open the history. A member without an enrolled key can administer the vault without being able to read a byte of it. Content. Adding a recipient ships end to end, and so does revoking one, via epoch rotation — see below.

For European teams

State the worst case first: Run402 is operated by Kychee, Inc., a Delaware (US) corporation, and the storage is Amazon S3, a US provider. If your transfer analysis assumes US legal process can reach both — assume it. What that process can extract is the table above, in full: the metadata rows, and the deploy lane's plaintext. It cannot extract vault keys or vault content, because we do not possess them. Run402 cannot decrypt your gitvault or repository history. Deployment artifacts remain a disclosed plaintext custody boundary. A compelled host is a special case of the malicious host every claim on this page is already stated against.

That shape — encrypted data held by a third-country provider while the keys stay only with you — is the transfer pattern the EDPB's post-Schrems II guidance on supplementary measures describes (Recommendations 01/2020, the encrypted-transfer case). We are not your counsel and this is not a compliance guarantee; it is the architecture, stated precisely so your own assessment can use it. The same goes for the GDPR more broadly: commit author names and emails are personal data, and in the vault lane we never see them — data minimization here is structural, not a policy.

The training question is the same claim from another angle. EU law gives you a machine-readable opt-out from text-and-data mining (CDSM Art. 4) that model providers must honor (AI Act, Art. 53). An opt-out is a legal claim you enforce against someone else's pipeline; encryption is a fact that needs no enforcing. Our future policies about your code cannot matter — including a future us, after an acquisition — because we never held the plaintext to change our minds about.

And sovereignty's other half — “what if a foreign order cuts us off?” — is answered by the exit ramp above, not by a flag on a data center. We could be compelled to stop serving a vault; we cannot be compelled to produce its plaintext. With the opt-in mirror, denial of service is not denial of your repository: your ciphertext, in a bucket you own, recoverable with your keys and an open-source tool, no server required.

We make no jurisdiction claims — no EU entity, no residency badge doing legal work, and no promise to notify you of legal process that a sealed order could forbid us to keep. The sentences here are the same ones the threat model grades, held to the same standard: narrow, true, and checkable.

Durability: how the host side holds your bytes

“Host-side loss” in the durability sentence below is doing real work, so here is what actually holds your ciphertext today — and what would have to fail before it is lost.

The honest limits, in the same voice as the rest of the page: today this is one bucket in one region of one AWS account, and the create/delete split constrains our code paths, not a fully compromised platform — which is exactly why the mirror above exists: it is the copy in a failure domain we cannot touch, and it is available today. Platform-side replication into a second, independent failure domain is design work in progress; it will be stated here only when it is live.

Keys, teammates, and losing yours

A product whose confidentiality rests on the platform holding zero vault keys has, by construction, no platform-side rescue for lost keys. So the durability sentence carries its qualifier in the sentence itself, never as a footnote:

The durability sentence

The vault protects source history from host-side loss while a principal keystore survives.

“A principal” — not “you”

How many keystores can open a vault is a state you control, and it starts at one. At creation exactly one principal holds an envelope — the creator — and a vault that stays that way has a genuine single point of failure: everything below about terminal loss applies to it.

It does not have to stay that way, and the mechanic is GitHub semantics: membership entails custody — a teammate who can read the code can recover the history. It ships today, end to end. A teammate enrols their own encryption key from the Source access card at console.run402.com/account (the private half never leaves their device, and never reaches us); the org records who should hold an envelope on each vault; a key-holding client wraps the vault key to them on its next git push or repos snapshot — envelopes ride the ordinary upload path, so a vault born creator-only is covered moments later — and run402 repos access shows exactly who is covered and who is still pending. From that point the vault has two independent openers, and losing one keystore no longer loses the history.

Taking a reader away is live too — epoch rotation (protocol rev 42, three rounds of adversarial review) shipped in client 4.45: repos access revoke-key removes a departed recipient and repos access repair rotates the vault to a new epoch, after which the revoked key opens nothing published from the rotation forward. repos access declare-exposure is the same ceremony for a key you believe compromised. Two honest limits, stated with their separate strengths: removal from the org denies the platform's reads immediately (operational), but the cryptographic guarantee is forward-only — ciphertext someone already fetched decrypts with the key they already held, because no rotation can unshare the past. Convergence is still client-driven — a teammate is wrapped when a key-holding client next pushes, not by the server the moment they enrol — which is exactly why repos access separates covered from pending. And the mirror above stays what it is: it multiplies where your ciphertext lives, while envelopes multiply who can open it.

What your client tells you

The single-principal case is where every vault starts, so the client states it to your face rather than burying it. run402 repos view 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

What survives what

You lostVault history is
Your repository key file, identity intactRecovered — open your own envelope
Your signing key, repository key heldReadable, read-only — you can decrypt and verify, not sign
Your trust pin onlyRecovered — re-verify from genesis against the recovery receipt
Your machine — but the keystore is backed up, or you mirror to your own bucket and kept the keysRecovered — restore the keystore, then git clone (or repos recover from the mirror)
Your whole keystore — but a covered teammate holds an envelope on this vaultRecovered — from their keystore, not from us
Every covered principal's keystore, every copyTerminal
The whole machine, keystore not backed up, nobody else coveredTerminal

Two practical consequences. Back up your keystore, and cover a second principal — either one keeps a lost machine from being terminal. And the recovery receipt that repos create 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.

Retention

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.

What GitVault is not

For agents

The vault routes, capabilities, and error codes are in /llms-full.txt and /openapi.json under /gitvault/v1; the CLI family is run402 repos (retired run402 gitvault spellings answer a typed error naming the replacement). 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.

Handoff. Resume. Invite. Join. — the same claim machinery, two products: kygit.com/handoff passes exact repository state to a DIFFERENT agent that continues alone (repos handoff / repos resume); kygit.com/invite brings a SECOND agent into the SAME state while the first keeps working, with a shared coordination room (repos invite / repos join). Both mint a single-use bearer key that never reaches this platform's servers; both are CLI/SDK-only by design, with no MCP tool for either verb pair.