git-zero-server 0.2.0

Allocation-free no_std Git smart/dumb HTTP serving and receive-pack primitives
Documentation
  • Coverage
  • 47.33%
    71 out of 150 items documented0 out of 62 items with examples
  • Size
  • Source code size: 222.7 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 2.3 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 6s Average build duration of successful builds.
  • all releases: 5s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • no-std-rs/git-zero
    1 0 1
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • pawelchcki

git-zero-server

Allocation-free Git smart and dumb HTTP server routing. Safe no_std Rust without alloc.

[dependencies]
git-zero-server = { version = "0.1.0", features = ["read"] }

See the API reference for routing, streaming, selection, and opt-in receive-pack APIs. read enables validated selection and revision storage; omit it for the default HEAD-only serving APIs.

Smart HTTP discovery/upload-pack and loose/packed dumb HTTP routing. The host supplies repository data, response output and HTTP framing. Depends on git-zero-core with only write enabled.

Part of git-zero.

Enable read for SelectionStorage and serve_with_selection, which select bounded reachable packs from a validated repository with independent refs.

SelectionBuffers<WANTS, OBJECTS, READ, PARSE> optionally owns those fixed arrays. Construction needs no allocator, and storage(graph_read_limit) lends a request view. Capacity and storage-byte queries expose its host memory budget. Hosts can still supply their own arrays through SelectionStorage.

V0 supports single ACK, multi_ack, multi_ack_detailed, side-band, side-band-64k and no-progress in SHA-1/SHA-256. Requests replay wants and common commit haves each round. Selection excludes their complete reachable history within one graph-read budget; complete-pack hooks can resend it. The hook's PackOutput sink frames ordinary pack bytes with a fixed 1 KiB buffer. No progress messages or ready/no-done shortcuts are emitted.

Parse and selection failures precede output. Any later error invalidates the whole response. Buffered hosts may replace it with write_upload_error on an empty sink; streaming hosts close failed transfers.

Request::git_protocol selects v0 (absent), v1 or v2. V1 adds its version packet before the v0 refs. V2 advertises ls-refs=unborn, fetch, agent and object format. Its commands require complete delimiter/flush framing. ls-refs supports symrefs, peels, unborn HEAD and multiple ref-prefix filters. Fetch supports wants, haves, no-progress, thin-pack/ofs-delta hints and include-tag; full non-delta packs satisfy the hints. Done requests omit ACKs and send a sideband packfile section; other rounds return ACKs or NAK and wait for done. Selection includes annotated and nested tags only when their final target is being sent, sharing one graph budget. Whole-pack hooks retain their documented HEAD-only restriction. Selection APIs advertise shallow deepen-relative filter (v0/v1) or fetch=shallow filter (v2). They support absolute and relative deepen depths, shallow/unshallow boundaries, blob:none and blob:limit filters, sharing the graph budget and object/index arrays. Relative depth uses the nearest client boundary in wanted history, matching Git 2.55. Unrelated branch boundaries remain intact; with no matching boundary, the wanted history is complete (within the graph budget). Full clients keep ordinary common-history pruning, including empty packs for no-op deepening. V0/v1 negotiate relative depth on the first want; only v2 accepts its argument. Without a depth, that negotiation leaves the ordinary fetch unchanged. Git 2.53 and earlier extend all reachable client boundaries, so this case deliberately differs; the versioned native oracle asserts both outcomes and identical imported objects. Depth requests retransmit their bounded closure; ordinary shallow haves stop common traversal at the client's boundary. Root commits at the depth limit stay shallow, matching Git. Ordinary selection entrypoints reject time/exclusion deepening; the separate revision API below enables it. The default advertised-ID want policy requires partial clones to use --no-checkout. Explicitly authorized hosts can enable lazy promised-object retrieval as described below. Legacy complete-pack APIs omit these capabilities and reject extended requests before output. Ready shortcuts and other optional v2 features stay unadvertised. V2 preflights every selected ls-refs line, including its attributes, against the 65,520-byte packet limit. Oversized attribute combinations return LimitExceeded before output; hosts can report a resource error. A narrower ref-prefix or a request without those attributes can still serve the same valid repository.

Hosts populate content_type and git_protocol. Uploads require application/x-git-upload-pack-request (415 otherwise). Known routes reject wrong methods (405), unavailable services (403), duplicate service/version fields, malformed escapes, encoded separators, dot components and control bytes. Paths accept zero or one leading slash; refs decode reserved bytes exactly once. Service parameter names must be literal. Hosts own HTTP framing, content decoding, authentication and request/output capacities. Native and Worker examples bound both compressed and decompressed gzip bodies.

stream::RequestBody accepts bounded fragments into caller-owned storage and checks declared length at EOF. Parsing still requires the complete bounded body; HTTP headers, gzip and chunked transfer decoding belong to the host. stream::ResponseEncoder::new prepares HEAD-only requests; with_selection prepares exact requested closures with the existing graph budgets. Preparation returns HTTP metadata after route/command/selection checks. Discovery, refs and ACKs must fit the caller's control buffer; pack and loose bodies use inline state and the slice supplied to each read. Keep the repository stable through EOF.

A successful read advances the cursor. Stage its bytes until the transport has accepted them, including across short writes, then pull again. Pausing makes no object reads; each pull performs at most one provider-body read. Raw reads are bounded by output capacity; sideband uses at most 1 KiB of internal staging. cancel is terminal and suppresses further reads and success terminators. A backend read can be retried with the same stable provider; failed-call bytes are invalid. Once HTTP headers are sent, unrecoverable errors require aborting the transport. These cursors do not implement HTTP range requests or serialized cross-process checkpoints. Separate cursors and buffers permit overlapping work.

WantOptions::all_visible_reachable(max_want_lines) is an explicit host grant for policy-aware selected responses. Use serve_with_selection_policy or ResponseEncoder::with_selection_policy / with_selection_storage_policy after routing/authentication and validation of the exact immutable public projection. Old entry points pass WantOptions::STRICT and retain their bytes and denial rules. Complete-pack/HEAD-only APIs do not grant this policy.

The grant permits any object kind in the validated union of visible HEAD/refs, including unadvertised commits, trees, blobs and nested tags. Physical backing objects outside that union are denied. This is stateless read authorization, not proof of an earlier promise. Discovery and content/cache keys grant no access: authorize every request before selection or cache lookup. Hosts retain immutable generations for as long as their promised downloads should work; changing a projection or collecting its objects can revoke previous wants.

V0/v1 advertise allow-reachable-sha1-in-want only with the explicit grant; v2 uses its existing fetch command. Client packets, headers and queries cannot turn it on. The exact opt-in discovery packets are preflighted before any output, including unborn HEAD/first-ref capability carriers. The added 29 bytes can make an old-limit ref fail with LimitExceeded; strict discovery limits are unchanged.

Every want occurrence, including duplicates, consumes the configured cap before output. Exhaustion returns LimitExceeded; insufficient distinct selection slots/scratch retain BufferTooSmall. Authorization uses the validated ID index without body reads. Existing request bytes, object/ref/selection arrays and the single structural graph-read budget remain separate finite limits. This is not a whole-request logical-work budget: parsing, bounded metadata scans and ordinary selection retain their existing accounting. A fresh request resets the counter. Explicit blob roots survive blob:none, enabling ordinary partial-clone checkout.

For synchronous selected-pack caching, serve_with_selected_pack uses the same route, want authorization and bounded selection as serve_with_selection. Its trusted callback receives the exact selected indices, read scratch and a PackOutput sink for this request. Call cache::selected::write using Selection::Indices(indices) and that sink. The cache retains only verified raw PACK bytes; ACKs, shallow updates, v2 sections and sideband framing are fresh. ACK-only rounds, boundary probes and non-pack routes never call the hook; an empty selected set still requires a valid empty pack. The old complete-pack hook and its HEAD-only cache key are separate APIs.

Every cache hit still follows authorization and graph traversal. A cache or backend error may follow provisional control output, so discard the whole output on failure. Buffer the complete response before sending HTTP success metadata, or abort an already started transport. A failed sink can leave a verified cache entry for a later request. This synchronous hook retains no cache borrow or response cursor; it adds no async storage or streaming-memory guarantee.

serve_with_selected_pack_policy accepts the same explicit WantOptions as the policy-aware uncached API. Its shared preparation rechecks the grant and want-occurrence cap before any callback/cache access, including a warmed hit. The original selected-pack hook remains strict. Neither cached bytes nor a client capability can enable a host policy.

The opt-in receive module implements receive-pack control framing, advertisements and report-status/v2 replies without std or allocation. ControlDecoder borrows one packet buffer and emits commands, shallow boundaries and negotiated push options as input arrives; the pack suffix is never consumed. All control bytes, command counts, shallow IDs and options have explicit budgets. Raw ref names preserve non-UTF-8 Git bytes. SHA-1 and SHA-256 are supported.

The host advertises only capabilities it implements, authenticates the request, stages commands/objects, resolves deltas and validates graph/ref updates before publishing. atomic requires an authoritative all-ref transaction. Report encodes the actual publication result, and write_progress can send sideband messages beforehand without acknowledging success. HTTP framing and storage are host operations. Existing read-only routing does not enable pushes implicitly.

Time and exclusion deepening

Enable read and explicitly supply RevisionStorage to serve_with_revisions, ResponseEncoder::with_revisions, or with_revision_storage. The convenience RevisionBuffers<WANTS, OBJECTS, READ, PARSE> owns ordinary selection storage plus exactly one state byte per object. storage(graph_read_limit, limits) accepts RevisionLimits { max_exclusions }. Keep separate buffers for overlapping requests. These APIs keep the strict advertised-ID want policy; they do not enable promised-object wants or selected-pack cache hooks.

Only revision-enabled v0/v1 discovery advertises deepen-since and deepen-not. V2 accepts these arguments under fetch=shallow on the same opt-in API. The Git protocol rules allow their combination and repeated exclusions, but forbid combining them with absolute depth. Timestamp cuts use committer times, include equality, and stop old ancestry paths even when a later ancestor has a newer time. Exclusions remove commit ancestry without removing shared trees/blobs needed by selected commits. Names resolve against visible HEAD/refs only, using Git's ref-name expansions; ambiguous names, raw IDs, revision expressions and missing refs fail. Annotated and symbolic refs work. Tree/blob exclusions have no commit-ancestry effect.

Existing client boundaries produce independent shallow/unshallow changes, including when a boundary body is absent from the pack. Newly unshallowed commits seed missing parents. blob:none and blob:limit can combine with these cuts. Revision requests with tree-depth or object-type filters currently return Unsupported; ordinary selection still supports those filters.

Wire timestamps follow deterministic unsigned base-zero conversion, including hex/octal/binary prefixes and signed unsigned conversion, with u64::MAX reserved as an invalid sentinel. The last repeated deepen-since wins. Canonical Git clients send decimal; acceptance of binary prefixes in reference Git depends on its C library. Historical committer dates use Git's tolerant decimal-prefix rule.

The graph-byte limit covers every structural read and reread across exclusions, date probing, boundary detection, common history, wanted closure and included tags. Exclusion limits count duplicate wire arguments before ref lookup. The caller bounds request bytes, want storage, object/index slots and read/parse scratch. Metadata scans have the same object-capacity bound as ordinary selection (quadratic worst case); ref resolution is bounded by the exclusion count and visible ref names. This API does not implement a total request-preparation CPU budget. Successful selection returns ascending provider indices; failures invalidate all output. Stream preparation returns no successful response on exhausted limits; failures after sending headers require transport abort.

Native and bounded Worker tests compare real Git with http-backend for both hashes and v0/v1/v2. Worker hosts opt into revision storage through the trusted loader's explicit revision settings; default handlers leave it disabled.

dumb::Publication adds immutable complete stored-pack publication with objects/info/packs and checksum-named .pack/.idx downloads. Preparation retains one caller-owned metadata entry per object; pack/index bytes stream without a complete buffer. It supports packed-only and loose fallthrough modes, shared route validation and independent response cancellation. Dispatch it before the ordinary handler over the exact same stable repository. Native and bounded Worker hosts pass unassisted real-Git packed/mixed clones in both object formats. See the publication guide.

ResponseEncoder::with_revision_storage_policy combines explicit revision buffers/limits with an independent host want grant. Revision capability cannot authorize an otherwise forbidden object, and permission does not enable revision cuts in ordinary constructors. Worker exports now expose these separate options with fixed state and graph/exclusion limits.

For asynchronous selected-pack storage, consume an unread ResponseEncoder with stream::preparation::PreparedResponse::new. Its selected slots and prepared control bytes remain borrowed; request bytes and temporary validation metadata do not. Keep the authorized repository immutable across every await. Persist only raw read_pack output, then attach a complete pack to verify its exact set before obtaining a response cursor. Non-pack responses use without_pack and never need a backend. The Worker adapter owns an isolated instance across awaits and demonstrates awaited KV/R2 publication.