← kygit.com

How it works

The technical version: what is encrypted and with what, what every observer can and cannot do, how to check each claim without trusting us — and the attack vectors we designed against, each with its honest residual. This page is the complete trust thesis; there is no separate write-up to chase down.

No invented cryptography. Standard primitives, named open-source implementations, one fixed suite, two independent verifiers, and a review process built to break it before you rely on it.

Trust model What happens on push The suite Two verifiers Test vectors The adversarial review Who sees what Keys and loss Security FAQ

The trust model in one sentence

Your machine and your keystore are trusted; our control plane and the storage bucket are untrusted for confidentiality — and every property below is stated against a malicious host and a malicious bucket, not a well-behaved one. Confidentiality survives that adversary structurally: there is no key material server-side. 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.

KyGit makes three promises with three different strengths, and refuses to blur them — conflating a cryptographic incapacity with an operational promise is exactly the overclaim this page exists to prevent:

The claims, graded
  1. Run402 cannot decrypt your gitvault or repository history. Deployment artifacts remain a disclosed plaintext custody boundary.
    Cryptographic, against us. Not a policy we follow — a capability we do not have. (The second sentence concerns run402's optional deploy path; a vault-only KyGit project never deploys, so its plaintext custody boundary is empty. See the FAQ.)
  2. Integrity and authenticity of your history are cryptographic for a client holding its trust pin: every head is signed by a writer key we never hold, every object is authenticated, and a substituted vault fails its genesis check.
    Freshness is the stated exception: it is pin-relative, not absolute — see the rollback question.
  3. Availability, durability, and retention are operational promises — retention is an operational promise of the platform, not a cryptographic guarantee against it (the host controls timestamps and bytes). What the protocol adds is that deletion is attested, two-phase, and attributable — silent loss is loud loss — and the opt-in mirror removes us from the availability picture entirely.

What happens when you git push

A vault is an append-only chain of signed, admitted heads over encrypted pack sets. Everything cryptographic happens on your machine, before any byte leaves it:

  1. The vault key is born on your machine. At creation the client draws a 32-byte repository key, K_repo, from the operating system's CSPRNG. It is never transmitted. Your keystore (~/.config/run402/gitvault/) also holds an Ed25519 signing keypair (the writer key) and an X25519 encryption keypair (your recipient key). We register only the public halves.
  2. Every object gets its own key. Each encrypted object's key is derived as k_obj = HKDF-SHA-256(K_repo, info), where info is a length-prefixed, domain-separated encoding of the protocol label (r402s/v0), the suite, the vault id, the key epoch, the object kind, and the object id. Object ids are single-use key labels: a retry re-reads and compares, it never re-encrypts under the same id — so the "same nonce, same key, different plaintext" failure class is excluded by construction, twice over.
  3. Content is sealed with an authenticated cipher. Bulk encryption is XChaCha20-Poly1305 with a fresh random 24-byte nonce per object. The frame is "R402S0" ‖ suite byte ‖ nonce ‖ ciphertext‖tag, and the additional authenticated data binds the vault id, object kind, object id, epoch, and suite — the frame header itself is authenticated, so a ciphertext cannot be replayed as a different object, into a different vault, or under a different epoch without failing its tag.
  4. The vault key travels only inside sealed envelopes. K_repo is wrapped to each authorized recipient's X25519 key with HPKE (RFC 9180, base mode: DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, ChaCha20-Poly1305). The HPKE info and AAD bind the protocol label, the suite, the recipient's key fingerprint, the sealing writer key, the vault id, and the epoch — an envelope for one vault, epoch, or recipient is not openable as any other. An envelope's signature is verified before any open is attempted.
  5. The push publishes a signed head. The head names the new generation, the previous head, and the SHA-256 of every stored ciphertext frame, serialized as RFC 8785 canonical JSON (strings-only profile — no numbers, no duplicate keys, nothing for two parsers to disagree about), and signed with your Ed25519 writer key under strict RFC 8032 verification rules. The server holds no writer key, so the server cannot mint history.
  6. Ciphertext goes straight to storage. Your repository ciphertext moves directly between your machine and storage — our servers never handle it. Uploads are create-only presigned PUTs into a bucket whose policy refuses overwrites; the control plane touches only heads and admission records, which are declared, disclosed metadata.
  7. The client verifies before it believes. 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 proof — a client that trusts the status code has handed the host a freshness oracle it was not supposed to have.

One consequence worth noticing: nothing the server stores or compares is derived from your plaintext un-keyed. Where the platform needs to compare (for example, that two captures commit to the same snapshot), it compares keyed HMAC digests — it can check equality without learning what is equal.

The cryptographic suite — boring on purpose

There is exactly one suite, r402s-1, and every primitive in it is a published standard with decades-old or heavily-reviewed lineage. We did not invent a primitive, a mode, or a construction; where a composed construction was needed (key wrapping), we adopted RFC 9180 HPKE as a named implementation rather than assembling it from parts — a rule enforced by CI gates in both codebases, not by good intentions.

PurposePrimitiveStandardTypeScript clientRust verifier
Signatures Ed25519, strict verification RFC 8032 @noble/curves (zip215: false pinned) ed25519-dalek verify_strict
Key wrapping HPKE base mode: DHKEM(X25519, HKDF-SHA256) + HKDF-SHA256 + ChaCha20-Poly1305 RFC 9180 @hpke/core rust-hpke, version-and-checksum pinned
Bulk encryption XChaCha20-Poly1305 (24-byte nonce) IETF draft, ChaCha20-Poly1305 family (RFC 8439) @noble/ciphers (Node-native backend where available) RustCrypto chacha20poly1305
Key derivation HKDF-SHA-256 RFC 5869 @noble/hashes RustCrypto hkdf
Hash / keyed digests SHA-256 / HMAC-SHA-256 FIPS 180-4 / RFC 2104 @noble/hashes RustCrypto
Canonical serialization JSON Canonicalization Scheme, strings-only strict profile RFC 8785 json-canonicalize + a strict parser a hand-written strict I-JSON front-end + JCS

Two implementations, one protocol — so you never take our client's word

Everything above is open source, and it is implemented twice, independently:

The non-sharing is the point: a differential verifier that reuses the code it is checking verifies nothing. And the second implementation is not decorative — it is load-bearing in the protocol's most dangerous ceremony: Every prune requires receipts from two independently built open-source implementations — one in TypeScript, one in Rust. Nothing is deleted on one codebase's opinion.

It is also what makes the exit ramp checkable by someone who trusts neither our servers nor our client: 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.

The honest shared assumption: both implementations implement the same specification, so a specification-level flaw would fool both. That is why the specification itself — not just the code — was the primary attack target of the review process below, and why the test vectors are generated and re-checked by a third, stdlib-Python reference implementation that shares code with neither.

The frozen test vectors

The protocol ships a frozen, deterministic vector set — vendored in the public repository — covering roughly three hundred vectors across two dozen classes: strict-parse accepts and rejects, per-field AAD rejection (flip any bound field and the frame must fail), chain continuity and rollback regression, HKDF and preimage derivations, HPKE interop golden files sealed by one implementation and opened by the other in both directions, tamper triples that must fail byte-for-byte, non-canonical-signature rejection, and state-machine edge tables. Deliberately, no prose hand-copies the total: a generated COUNTS.json carries it, and the verifier asserts prose-free counts against the vector file itself.

How it was reviewed: two frontier models, opposite sides of the table

Before the wire format froze, we did to this design what we expect attackers to do — on the record, round after round, until the attacks stopped landing on the cryptography.

The protocol was drafted and defended by one frontier model and attacked by another, from a rival lab, with no stake in being agreeable: Anthropic's Fable 5 on the design side, OpenAI's GPT‑5.6 Pro as the standing hostile reviewer — rival labs' frontier reasoning models, pointed at each other. The reviewer's charter is blunt: the client trusts nothing the server says beyond what a signature or an AEAD tag forces it to, and the reviewer's job is to break that — chain-integrity and rollback attacks, AEAD and nonce discipline, HPKE misbinding and cross-context replay, key-derivation domain separation, signature forgery and malleability, metadata leaks beyond the disclosed set, downgrade paths, the two-implementation claim itself, and the recovery story.

Forty-nine recorded review rounds came out of that charter, and the record is not a parade of passes — it reads the way real security work reads:

The process did not stop at the freeze. Every capability added since — multi-recipient envelopes, epoch rotation, recovery-code custody — went through its own adversarial rounds under the same rules before shipping, and the findings above that named honest limits (rotation is forward-only; recipient authenticity is operator-confirmed, not host-blind) are printed on this page and in the threat model rather than smoothed over.

Model-vs-model review is a complement to open source, not a substitute for human eyes — which is exactly why the protocol, the threat model, the schemas, the vectors, and both implementations are public. Everything the reviewer attacked, you can attack.

Who sees what

Content is hidden; structure is not. This is not zero-knowledge hosting, and the honest way to say so is to enumerate the boundary. The full observer-by-observer matrix lives in the threat model; the shape of it:

ObserverSeesCannot
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; policy state; keyed digests. A closed, enumerated list — anything beyond it is a protocol violation, not an implementation detail. Decrypt anything. See branch names, commit messages, file paths, or commit ids. Forge a head (no writer key). Produce an un-keyed digest of your plaintext, because none exists.
Bucket operator (AWS) The same objects minus control-plane context, plus access patterns. Decrypt; forge. It can delete or withhold bytes — the availability rows above.
Network Endpoints, sizes, timing. TLS covers the rest. Read object ids or content.
Your agent's deploy delegate Heads, ciphertext, receipts for what it uploads — enough to push. Prune, compact, repair, change policy, or override activation. Those are owner powers, and a delegate is structurally never an owner: nothing the agent holds can destroy the history it wrote.
An org member without an envelope Ciphertext and control-plane state; can administer the vault. Read a byte of content. No envelope is addressed to them.
A removed recipient Nothing new — their credentials stop working. Be made to forget. What they already fetched or opened stays decryptable to them; only an epoch rotation closes the forward direction. See the FAQ.

Keys, envelopes, and what losing them means

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:

The durability sentence

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

Every vault starts with exactly one keystore that can open it, and the client says so to your face — run402 repos view prints, verbatim: "whole-machine or whole-keystore loss is terminal for vault history until human envelopes ship". It does not have to stay that way:

The security FAQ: every attack vector, answered

"Why is this secure?" decomposes into specific attacks. Here is each one we designed against — what stops it, and, where a residual remains, the residual stated instead of hidden. The questions are grouped by what the attacker is trying to do.

Reading your code

You run the servers. Why can't you just read my repository?

Because there is nothing on our side that can. Encryption happens on your machine before upload; K_repo and every derived object key exist only in your keystore and in HPKE envelopes sealed to authorized recipients' keys; the substrate stores ciphertext frames plus the enumerated metadata and holds zero vault keys. This is a capability gap, not a policy: there is no configuration of our servers, no admin tool, and no insider with a database password that changes it. The claim is deliberately scoped — we can read what you explicitly deploy through run402's separate deploy path (below) — and that narrowness is what makes the vault claim checkable rather than rhetorical.

Couldn't a malicious insider — or a hacked run402 — start collecting keys?

Not from the protocol: no message in r402s/v0 ever carries key material to us, so there is nothing for a compromised server to collect. A compromised server gets what the observer table says and no more. The real vector is the client — shipping you a modified build that exfiltrates keys — and we state it rather than pretend it away: the client is open source, its releases are pinned and inspectable, a second independent implementation exists to check its outputs, and a reader who audits or pins their client keeps the machine-side trust assumption in their own hands. That residual is common to every end-to-end-encrypted product ever shipped; the difference is whether the vendor says it out loud.

What can a subpoena or government order actually get?

State the worst case first: Run402 is operated by a US company and the bucket is Amazon S3, a US provider — assume legal process reaches both. An order served on us can compel at most what we possess: the enumerated vault-lane metadata (who, when, how big, how often) and, for projects that deploy, the deploy lane's plaintext artifacts. It cannot compel vault keys, vault content, commit ids, branch names, or file paths — none of it exists on our side to produce. A compelled host is a strictly weaker adversary than the malicious host every claim on this page is already stated against. Two honesty notes: we make no promise to notify you of legal process (a sealed order could forbid exactly that), and we do not claim the metadata is harmless — it is real information, and it is exactly what an order would yield.

Will my code end up in somebody's training set?

Not from here. There is no plaintext on our side to index, embed, or train on — and that holds against a future us too: a policy change, an acquisition, or a partnership cannot un-encrypt ciphertext. Meanwhile the product is built for your agents, not against them: an agent you authorize holds its own envelope and works normally, while everyone else's AI — including ours — sees ciphertext. Selective disclosure, not abstinence.

What about AWS — the bucket operator — or someone on the network?

The bucket operator sees the same ciphertext objects, minus our control-plane context, plus access patterns; it cannot decrypt or forge anything, for the same reason we cannot. The network sees endpoints, sizes, and timing under TLS. Both can deny service or destroy bytes — which is an availability question, answered by the create-only storage posture and the mirror (below), never a confidentiality one.

So is this zero-knowledge? What exactly do you learn about me?

No — this is not zero-knowledge hosting, and we say so on every page. The platform learns the enumerated metadata list: that the vault exists and whose it is, ciphertext sizes and object ids, generations and push cadence, access times, policy state, and the structural skeleton of maintenance. The list is closed — the protocol treats any plaintext-structured object beyond it as a violation, not a detail. What never appears in it: content, branch names, commit messages, file paths, commit ids, or any un-keyed digest of plaintext.

Can ciphertext sizes and timing leak my content?

Sizes and cadence are visible, and we disclose that rather than pad it away — traffic-shaping countermeasures are not part of v0, so an observer can estimate how big your repository is and how actively you push. What an observer cannot do is the sharper attack this category usually hides: there is no cross-vault deduplication oracle and no compression oracle against the server, because nothing the server can compare is derived from plaintext un-keyed — every digest the platform can compare is keyed with HMAC. Two vaults containing the same file look unrelated to us.

Can someone probe whether a given vault exists?

No. Unauthorized, nonexistent, and wrong-project requests all return one identical refusal, so no response distinguishes "exists but not yours" from "does not exist."

What is the "deploy lane" you keep disclosing?

KyGit is run402's git vault under its own front door, and run402 is also a deployment platform. If — and only if — you choose to deploy a project, the files you publish pass through run402's content store in plaintext, custodially, because serving a website requires reading it. That is the second sentence of the first claim: deployment artifacts remain a disclosed plaintext custody boundary. Deploying is an explicit act — it decrypts exactly what you chose to publish — and the deploy path carries its own rule — Activation requires vault admission by default; an explicit, audited override can bypass it. A deploy cannot silently outrun its own history capture. A vault-only KyGit project never deploys anything, and then the custody boundary is empty: there is no plaintext of yours anywhere on our side.

Tampering with your history

Can run402 forge a commit, a branch, or an entire history?

No. Every head is signed with your Ed25519 writer key, which never leaves your keystore; the server registers only the public half. Signature verification is strict RFC 8032 — non-canonical encodings and small-order points are rejected, and the vector set includes a ZIP215-only signature that must fail — so there is no malleable acceptance path to exploit. A server that cannot sign cannot mint history; the strongest thing a malicious host can do is refuse to show you the newest history you wrote, which is the next question.

Can the server roll me back, or show different histories to different people?

Not below your pin. Your client keeps a local trust pin at the newest verified generation; a served chain that regresses below it fails loudly (GENERATION_REGRESSION), and every link is verified — signatures, hashes, continuity — so a fork or a splice fails structurally. The honest limit, stated rather than hidden: freshness is pin-relative. A malicious host can serve a stale but authentic suffix, and no client can cryptographically prove the host is not withholding a newer one — that impossibility is general to hosted storage, not particular to us. The mitigations are the read-back rule (your own push is confirmed against storage before the client believes it), pins that only move forward, and the mirror, which dual-pushes on every snapshot so the copy in your bucket tracks the newest generation you published.

Could the server swap in a fabricated vault that looks internally consistent?

A fabricated chain can be internally consistent — the attacker signs it with their own key — but it cannot be your vault: your client pins your writer and genesis identity, and the printed recovery receipt binds the vault's genesis hash and creator fingerprints, so a substituted vault is refused on mismatch. This is exactly why the receipt is worth keeping copies of, and why a recovery without receipt or pin is labeled unauthenticated salvage rather than passed off as verified.

Can a ciphertext or key envelope be replayed somewhere it wasn't meant for?

No — context binding is everywhere. A content frame's AAD binds vault id, object kind, object id, epoch, and suite; move it anywhere else and the tag fails. An envelope's HPKE info/AAD bind the protocol label, suite, recipient fingerprint, sealing key, vault id, epoch, and (during rotation) the specific rotation attempt; it opens in exactly one context. The vector set carries per-field rejection vectors — each bound field is flipped in turn and the operation must fail — plus "near-neighbor" encodings of the binding data that must not collide.

What if the host corrupts or truncates a single object?

It gets caught, attributably. Every object is AEAD-authenticated, its framed hash is named in a signed head, and storage is create-only — there is no "latest" pointer to clobber, so corruption cannot hide behind an overwrite; it can only appear as a loud, detectable gap in a chain your client (or the independent Rust verifier) walks. run402 repos fsck verifies from your pin to the newest listed generation and fails closed on a regression, a gap, or a descriptor it cannot validate.

Couldn't run402 add itself as a recipient of my vault?

This exact attack was found by the adversarial review — round one of the epoch-rotation review showed an earlier draft would have let the platform's member-key directory smuggle a host-controlled key into a recipient set — and the design was rejected and rebuilt around it. As shipped: your client seals envelopes only to recipient keys the vault owner has confirmed, pins recipient keys on first use, and refuses a rotation whose recipient set does not exactly match the owner-confirmed state. The honest boundary, printed in the threat model verbatim: recipient authenticity today is operator-confirmed, not host-blind — your confirmation is the thing standing between a malicious directory and your recipient set, and an owner-signed recipient registry is the named hardening before any stronger claim gets made.

Breaking the cryptography itself

Did you roll your own crypto?

No. Every primitive is a published standard (Ed25519, HPKE per RFC 9180, XChaCha20-Poly1305, HKDF-SHA-256, SHA-256/HMAC, RFC 8785 canonical JSON) in mainstream open-source implementations — the noble/scure family and @hpke/core on the TypeScript side; RustCrypto, rust-hpke, and ed25519-dalek on the Rust side. What is ours is the protocol that composes them — object model, bindings, state machines — and that is precisely the layer the adversarial review spent forty-nine rounds attacking, the vector set freezes, and two independent implementations cross-check.

Nonce reuse is the classic ChaCha20-Poly1305 failure. Why not here?

Three independent layers would each have to fail. Nonces are 24 bytes (the XChaCha extension exists precisely to make random nonces safe at scale) and drawn fresh from the OS CSPRNG. Every object is encrypted under its own HKDF-derived key, so a nonce collision across objects would be harmless anyway. And object ids are single-use key labels — a retry or resumed upload re-reads and compares rather than re-encrypting under the same id, which removes the concurrency-and-retry path by which "same key, same nonce, different plaintext" actually happens in production systems.

Downgrade attacks? Suite confusion?

There is nothing to downgrade to. One suite exists (r402s-1); the suite identifier is authenticated inside every frame and envelope; no negotiation happens anywhere in the protocol. A weaker-suite attack requires a second suite, and introducing one is a numbered protocol revision with its own review and vectors, not a runtime code path.

Signature malleability, or two parsers disagreeing about what was signed?

Both are addressed at the root. Everything signed is serialized as RFC 8785 canonical JSON under a strict, strings-only profile — no numbers, no duplicate keys, no lone surrogates — so there is one byte sequence per object and nothing for a signer and verifier to disagree about. Verification is strict RFC 8032 with the permissive-verification mode explicitly pinned off and a conformance vector proving the permissive-only signature is refused. And because parser differentials are exactly the bug class that sneaks past a single implementation, the Rust verifier carries its own hand-written strict parser and replays the same accept/reject vectors.

Can a ciphertext be crafted to open validly under two different keys?

The protocol does not rely on the AEAD to prevent that. Envelopes are signature-verified before any open is attempted; the epoch key carries a signed commitment that the opened key must reproduce; and the client refuses — with named, distinct diagnostics — an envelope whose opened key fails the commitment or fails to decrypt the generation it claims to carry. A wrong-key open therefore cannot be silently accepted as content.

What about quantum computers?

An honest answer instead of a buzzword: r402s-1 is a classical suite. X25519 key agreement is not post-quantum, so an adversary who records your ciphertext today and obtains a cryptographically relevant quantum computer later could attack the recorded key envelopes — "harvest now, decrypt later" applies to KyGit as it applies to TLS and to every deployed E2E system of this generation. The suite identifier exists in every frame and envelope so that a successor suite (e.g. a hybrid post-quantum KEM, which HPKE accommodates) can be introduced as a deliberate, reviewed, vectored protocol revision. We will make a post-quantum claim when we ship one, and not before.

People, keys, and machines

What if I lose my keys? Be straight with me.

Straight: a product whose confidentiality rests on us holding zero vault keys has, by construction, no platform-side rescue for lost keys. The durability sentence is "The vault protects source history from host-side loss while a principal keystore survives" — and the "while" clause is the claim, not a caveat. What survives what: losing the repository key file with your identity intact is recovered from your own envelope; losing only the signing key leaves history readable but read-only; losing your whole machine is recovered if the keystore is backed up or a covered teammate holds an envelope — and terminal if every covered keystore is gone, which the client tells you to your face rather than in a footnote. Practical consequences: back up ~/.config/run402/gitvault/, and cover a second principal. Either one keeps a lost machine from being terminal.

A teammate leaves — or their key is compromised. Then what?

Two different removals, two different strengths, stated separately. Removing their org membership is immediate and operational: the platform stops serving them heads, ciphertext, and envelopes. Making the removal cryptographic is epoch rotation, live today: run402 repos access revoke-key removes the recipient, run402 repos access repair rotates the vault to a fresh epoch key sealed only to the remaining confirmed recipients, and run402 repos access declare-exposure is the same ceremony for a key you believe compromised. After an admitted rotation, the removed key opens nothing published from the rotation forward. The limit we will not launder: rotation is forward-only. Ciphertext someone already fetched decrypts with the key they already held — no rotation, here or anywhere, unshares what someone has seen. Copy that promises retroactive un-sharing is lying to you.

My agents push to this thing unattended. What can a compromised agent do?

Write, not destroy. A deploy delegate holds exactly {read heads, read ciphertext, upload, publish} — enough to push history, never to prune, compact, repair, change policy, or override activation, because those are owner powers and a delegate is structurally never an owner. Nothing the agent holds can delete the history it wrote; a compromised agent credential is revoked with one call, and the vault's history survives everything the credential could reach. Compare that with handing an agent a forge account whose token can force-push and delete repositories.

What if my own machine is compromised?

Then the attacker is on the trusted side of the boundary, and no end-to-end design survives that intact — an attacker with your keystore is you, exactly as an attacker with your SSH key is you to GitHub. What the design still gives you: the blast radius is bounded to vaults whose envelopes that keystore holds; repos access declare-exposure plus a rotation re-keys a vault forward once you have evicted the attacker; and because the vault is a second remote, your primary host and your teammates' pins make a quiet history rewrite by the attacker detectable rather than silent.

What if your npm package gets backdoored?

Supply chain is the honest soft underbelly of every developer tool, so here is the posture rather than a promise: the client and its crypto dependencies are open source and pinned; the Rust verifier is built --locked against a committed lockfile with checksummed release binaries, shares no dependencies with the npm stack, and can re-verify what the client produced — so a backdoored client that forges or mangles vault state is detectable by an implementation the same compromise cannot reach. A backdoored client that exfiltrates your keys remains the client-side residual stated above; pinning versions, auditing diffs, or building from source keeps that trust decision in your hands.

Availability, durability, and us disappearing

What happens when KyGit is down?

Pushing to KyGit needs KyGit up — we will not pretend otherwise, and we make no uptime claims here. What is structurally true: commits are local, so an outage delays publishing, never work; the byte path has no git application server in it (client ↔ storage via presigned URLs, with only the small admission surface on us); and a second remote in an independent failure domain means your primary host's outage and ours are uncorrelated — when one is down, the other keeps taking pushes. Reads have a stronger story: with the mirror, recovery and cloning need no server of ours at all.

What stops you deleting my bytes — by bug or by malice?

Against our own bugs and our own code: the bucket policy — enforced by AWS, not by our application — accepts only create-only conditional writes, refuses bulk deletion outright, and allows no lifecycle expiry; create and delete are separate code paths with separate powers; every deletion is two-phase, attested, receipted, and requires the two independent verifier receipts; unreachable history stays recoverable for at least 90 days measured from storage-commit time; and every storage operation is independently logged. The bytes themselves rest on storage designed for eleven nines of object durability. Against a genuinely malicious us, we say the true thing: retention is an operational promise of the platform, not a cryptographic guarantee against it — no cryptography can stop a storage provider from destroying what it stores. What the protocol guarantees instead is attribution: a deletion that was not owner-authorized leaves a ledger gap your own client detects. Silent loss is loud loss — and the mirror means loss on our side is not loss.

What if I stop paying?

Nothing dramatic. Read-side vault routes are never lifecycle-gated: through the billing grace ladder your vault stays readable and cloneable; what a lapsed lease gates is control-plane writes. And the mirror means your history is never hostage to a billing state in the first place.

What if Kychee shuts down, gets acquired, or turns evil?

The three scenarios share one answer, because the design never depended on our goodwill: the exit ramp. Your ciphertext replicates to a bucket you own. We can't decrypt your repository history — and we can't hold it either. 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. And acquisition changes nothing about ciphertext: a future owner of this company inherits our incapacity along with our servers — we hold no key for them to change our minds with. The honest limits ride along: offline recovery proves validity, never freshness, and without your keys, mirrored ciphertext recovers nothing.

Checking us

Why should I believe any of this page?

You shouldn't have to — that is the design goal. The wire protocol, the threat model, the schemas, the frozen vectors, the TypeScript crypto core, and the Rust verifier are all public. Run the verifier against your own vault's bucket export; replay the vectors; diff the claims on this page against the threat model. Even this page is machine-gated: a CI truth gate checks every KyGit page against the published client's own capability manifest on every deploy and every day, so the marketing cannot quietly outrun the software.

What do you explicitly NOT claim?

Every one of these is a sentence a louder page would have rounded up. The narrowness is the claim: what remains, you can check.