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.
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.
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.
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.
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.
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.
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 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 every key. The platform can read what you deployed. It cannot read what you did not.
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.
# 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
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.)
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 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.
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.
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. |
| 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. |
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.
“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.
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 vault protects source history from host-side loss while a principal keystore survives.
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.
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
| 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 |
| Your machine — but the keystore is backed up, or you mirror to your own bucket and kept the keys | Recovered — restore the keystore, then git clone (or repos recover from the mirror) |
| Your whole keystore — but a covered teammate holds an envelope on this vault | Recovered — from their keystore, not from us |
| Every covered principal's keystore, every copy | Terminal |
| The whole machine, keystore not backed up, nobody else covered | Terminal |
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.
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.
revoke-key + access repair rotate the epoch). But revocation is forward-only: ciphertext a recipient already fetched decrypts with the key they already held. No rotation, here or anywhere, unshares what someone has seen.
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.