hushgit join waitlist

Open protocol shape

HushGit is a zero-knowledge Git hosting protocol: clients encrypt and sign locally, the server stores opaque ciphertext and signed events, and clients verify witnessed transparency proofs for server-backed state.

MVP architecture

Trusted clients, desktop apps, and the CLI own Git transport, local diff and review rendering, device key storage, and repo content key unwrapping. They send encrypted chunks and signed events to an untrusted HushGit server that stores opaque metadata, encrypted pack chunks, append-only signed event logs, and transparency proof material.

Source of truth

This page is the short public overview. The implementation-aligned draft is the protocol spec v0.1, and the machine-readable HTTP contract is api/openapi.yaml. The public architecture and diagrams page maps the trust boundaries, key hierarchy, signed event log, transparency layer, server storage model, and CI runner flow in more detail. HushGit is still pre-deployment, so incompatible protocol changes update the spec, OpenAPI, test vectors, and implementation directly.

Client-side Git on a server-backed remote

The Rust CLI now creates, lists, clones, and deletes server-backed encrypted repositories, and the git-remote-hushgit helper drives normal git clone, git fetch, git pull, and git push through hushgit::<server>/<repo_id> URLs bound to a stored local profile. The encrypted signed refs and HEAD, the encrypted manifest repo object, and the encrypted Git pack chunks are fetched from the server, decrypted by the helper, and reassembled into a temporary bare mirror so Git only ever sees plaintext inside the local client process.

Server storage model

The server splits a relational metadata plane (Postgres via sqlx) from a content plane (an S3-compatible object store wrapped by a BlobStore abstraction). Postgres holds users, sessions, opaque repo identity rows, chunk descriptors, repo object descriptors for the encrypted manifest object, the append-only signed event log with signed parent-event links, write-token challenges, push locks, approved runner signing keys, runner job rows and attempts, runner job result chunk descriptors, a separate append-only runner event stream, transparency leaves, subject states, checkpoints, and witness signatures. The object store holds encrypted pack chunks and encrypted manifest repo objects, addressed only by opaque keys. Keyring updates ride the signed-event log as keyring-change events. The HTTP surface is pinned by OpenAPI and exposes native repo, chunk, repo-object, event, runner, runner-job, and transparency routes alongside browser-CORS read paths. The standalone witness process serves the cosign endpoint outside the server process. The server may index opaque identifiers, but plaintext names and Git metadata stay inside encrypted manifests unless explicitly accepted in the threat model.

Transparency and witnesses

Account, repository, runner, catalog/list, and proof-bearing byte responses carry transparency bundles. A bundle includes a signed checkpoint, witness quorum, optional consistency proof from the client's previous checkpoint, a subject-map proof, private leaf openings, inclusion proofs, and a policy hash. Native clients, the CLI, git remote helper, runner, desktop sidecar, and SDK verification path fail closed when the proof does not match the locally pinned trust anchor and subject state. CLI monitor commands can export evidence bundles and verify them offline.

Account and device trust boundary

Account login proves account access, not repository trust. Password registration and login use OPAQUE, browser-first signup creates only a convenience account, and native bootstrap consumes a fresh OPAQUE password proof while the native CLI or desktop path creates repository-capable signing and wrapping keys. Repository-sensitive writes require a native-trusted device or a narrow recovery-key signature, not just a session token: repository creation, chunk upload or deletion, repo-object upload, event append, keyring changes, runner registration approval or revocation, and runner job create or cancel. Approved runner keys stay repo-scoped and authorize only runner routes.

Encrypted metadata boundary

Repo display names, branch names, tags, commit messages, file paths, pull request text, review state, and CI job names or logs that may reveal code are encrypted by default. The server can see opaque account, organization, repo, repo-object, event, chunk, runner, runner-job, attempt, and lease identifiers; envelope headers; ciphertext sizes and object digests; transport, event-log, job, and audit timestamps; approximate push, pull, review, runner, and API activity frequency; and billing or admin relationships needed to operate the service. Lease ids are secret attempt handles even though they remain opaque to the server.

implementation

Rust owns the security-critical path: git-remote-hushgit and hushgit-git for transport, hushgit-core for client orchestration (device store, keyring, sessions, remote ops), hushgit-native-client for shared native verification, hushgit-protocol for shared IDs, event, manifest, keyring, and transparency schemas plus CBOR encoding, hushgit-crypto for envelopes, hushgit-keys for the key hierarchy, hushgit-events for signed events, hushgit-transparency for checkpoint, witness, map, and evidence verification, hushgit-storage for chunk-store backends, hushgit-server plus OpenAPI for the server API, hushgit-witness for external cosigning, and hushgit-runner for self-custodied CI.