Referencia Zigconduit-zig (SDK consumido)

zig/server.zig

Declaraciones públicas de zig/server.zig (conduit-zig (SDK consumido)).

ImplementadoSin versión del tren todavía· generada desde apps/docs/generated/zig/conduit.json

Página generada desde conduit@conduit-0.1.0-f7J_7zg5DwCsHBHItwdOz9ZVq8cvH7ceFsk7-m3-T61t (zig/root.zig). No se edita a mano: bun run docs:gen la regenera y bun run docs:check falla si difiere.

The receiving side of conduit wire v1 (spec §4, §7), transport-agnostic.

An HTTP binding (HTTP/1.1 in http.zig, HTTP/3 in styx's IngestSink) parses the request head and calls begin; the server authenticates, routes and admits the request before a single body byte is read:

  • .reply: done, the response JSON is already in out;
  • .json: read at most max_request_json bytes of body, then finishJson;
  • .chunk: a pinned ChunkWriter; stream the body into write, then finish (or cancel if the connection drops).

Invariants (styx dec-0117):

  • deny by default: no Authorizer ⇒ it does not compile; no token ⇒ 401 before routing; a session answers only its creator (others get 404);
  • quotas reserved at create: sessions per subject and in total, staging bytes in total and per subject (zkit.safety.Budget; one subject never holds the whole staging), and session memory through a BudgetAllocator (I4);
  • revocation: abortOwner retires every session of a revoked credential (quotas back, .part unlinked) at once;
  • staging files are <uploadId>.part created exclusive, 0600, relative to the staging root's fd and never through a symlink (I7);
  • references that outlive the lock are generational handles (zkit.safety.TypedSlab) plus a pin count — an abort while a chunk is streaming frees the session only when the writer lets go (I9);
  • staging space is reserved on disk at create, after the quota is committed and outside the lock (storage.reserve: ENOSPC → 507, everything admission counted rolled back); chunk bodies go straight to disk, hashed on the fly: chunks ≥ storage.Options.direct_min bypass the page cache (O_DIRECT + io_uring on Linux, F_NOCACHE on macOS, with runtime fallbacks down to buffered pwrite), the rest with pwrite. wire v1: the file SHA-256 advances over the in-order prefix through a zkit.ReorderBuffer whose bound is the admission window (backpressure, 429 window_full), re-reading that prefix from disk. wire v2 (spec/wire-v2.md, negotiated per session by "protocol":2): one BLAKE3 pass per byte — each chunk is hashed as a subtree at its offset, group by group, and checked against Conduit-Chunk-Cv; the file root is the combination of the chunk CVs at complete. No second hash, no re-read, chunks in any order (the window bounds requests in flight).

server.Root

const · línea 59

pub const Root = zkit.safety.fs.Root

Staging directory handle (fd-relative, zkit.safety.fs). Re-exported so a consumer opens the root with the same type the server takes, whatever other zkit version it links.

server.Op

type · línea 63

pub const Op = enum

Sin ///.

server.Grant

type · línea 65

pub const Grant = struct

Sin ///.

server.Authorizer

type · línea 94

pub const Authorizer = struct

Decides whether token may perform op (on upload_id, when the route has one). null = deny. Implementations must be thread-safe.

server.Authorizer.authorize

fn · línea 106

pub fn authorize(a: Authorizer, token: []const u8, op: Op, upload_id: ?[]const u8) ?Grant

Sin ///.

server.Authorizer.stillValid

fn · línea 110

pub fn stillValid(a: Authorizer, grant: Grant) bool

Sin ///.

server.StaticTokens

type · línea 118

pub const StaticTokens = struct

Static bearer tokens (reference server, CLI serve, tests). Tokens are kept only as SHA-256 and compared in constant time.

server.StaticTokens.Entry

type · línea 119

pub const Entry = struct

Sin ///.

server.StaticTokens.entry

fn · línea 122

pub fn entry(token: []const u8, subject_label: []const u8) Entry

Sin ///.

server.StaticTokens.authorizer

fn · línea 126

pub fn authorizer(self: *const StaticTokens) Authorizer

Sin ///.

server.Completed

type · línea 145

pub const Completed = struct

Sin ///.

server.FileDigest

type · línea 162

pub const FileDigest = union

Sin ///.

server.FileDigest.bytes

fn · línea 166

pub fn bytes(d: FileDigest) [32]u8

Sin ///.

server.FileDigest.hex

fn · línea 172

pub fn hex(d: FileDigest) [64]u8

Sin ///.

server.Finalizer

type · línea 180

pub const Finalizer = struct

Moves a finished upload to its final place and returns its key (the finalPath of the response, ≤ max_key_len). Called without locks held, once per session.

server.Finalizer.max_key_len

const · línea 184

pub const max_key_len = 256

Sin ///.

server.rename_to_digest

const · línea 193

pub const rename_to_digest: Finalizer = .{ .finalizeFn = renameToDigest }

Default: rename <uploadId>.part → <digest hex> inside the staging root (content-addressed: the SHA-256 of a v1 upload, the BLAKE3 root of a v2 one; the same content twice under the same protocol lands on the same name). Durable: the data is synced before the rename and the directory after, so a crash leaves either the .part or the complete final file, never a final name over partial data.

server.Limits

type · línea 209

pub const Limits = struct

Sin ///.

server.Limits.max_window

const · línea 280

pub const max_window = 1024

Sin ///.

server.RequestHead

type · línea 285

pub const RequestHead = struct

Sin ///.

server.Begin

type · línea 299

pub const Begin = union

Sin ///.

server.JsonRequest

type · línea 308

pub const JsonRequest = struct

Sin ///.

server.Capacity

type · línea 395

pub const Capacity = struct

Optional room check of the destination, for limits the server does not see: what finished uploads already occupy, free space on the volume. Called under the server lock right before a create reserves its staging, with the size it declares and the staging the open sessions already hold; false refuses it with 507 insufficient_storage (retryable) and nothing reserved. Must be cheap (it runs under the lock) and must not call back into the server. The staging file itself is allocated after the lock is released (§8.1), so a free-space reading may not yet reflect creates admitted just before: staging_in_use counts them.

server.Config

type · línea 400

pub const Config = struct

Sin ///.

server.Server

type · línea 442

pub const Server = struct

Sin ///.

server.Server.InitError

const · línea 469

pub const InitError = Allocator.Error || error{InvalidLimits}

Sin ///.

server.Server.init

fn · línea 477

pub fn init(self: *Server, gpa: Allocator, cfg: Config) InitError!void

self must not move after init (the budget allocator is referenced by the slab). Use create for a heap instance.

Sessions live in memory, so a staging file left under the root by a previous run can never be completed: init removes them (removeOrphanStaging) and counts them in orphans_removed.

server.Server.deinit

fn · línea 512

pub fn deinit(self: *Server) zkit.safety.LeakReport

Drops every session (unfinished staging files are deleted) and returns the memory report of the session allocator (isClean() in tests). Callers must have finished or cancelled all chunk writers.

server.Server.storageStats

fn · línea 537

pub fn storageStats(self: *const Server) storage.Stats

What the storage layer did so far (reservations, direct writes and each fallback taken).

server.Server.stagingInUse

fn · línea 541

pub fn stagingInUse(self: *const Server) u64

Sin ///.

server.Server.subjectStagingInUse

fn · línea 546

pub fn subjectStagingInUse(self: *Server, subject: [32]u8) u64

Staging bytes the open sessions of subject hold.

server.Server.ownerUploads

fn · línea 554

pub fn ownerUploads(self: *Server, owner: [32]u8) u32

Uploads created so far under owner (grants with max_uploads).

server.Server.sessionCount

fn · línea 560

pub fn sessionCount(self: *Server) u32

Sin ///.

server.Server.begin

fn · línea 568

pub fn begin(self: *Server, head: RequestHead, out: *Writer) Begin

Sin ///.

server.Server.finishJson

fn · línea 590

pub fn finishJson(self: *Server, req: JsonRequest, body: []const u8, out: *Writer) u16

Sin ///.

server.Server.abortOwner

fn · línea 1232

pub fn abortOwner(self: *Server, owner: [32]u8) u32

Retire every session created under a grant whose ownerKey() is owner — the consumer revoked that credential. Its upload count (Grant.max_uploads) is forgotten: the consumer must not authorize that credential again. Unfinished staging files are deleted and quotas released at once; a chunk still streaming into one gets 404 when it ends. A session in the middle of finalizing is left to finish (its file is being renamed) and retired right after. Returns how many were retired now. Idempotent.

server.Server.sweep

fn · línea 1264

pub fn sweep(self: *Server) u32

Retire sessions idle for longer than idle_ttl_ns (open or finished). Returns how many were retired. Call periodically.

server.ChunkWriter

type · línea 1384

pub const ChunkWriter = struct

Sin ///.

server.ChunkWriter.WriteError

const · línea 1427

pub const WriteError = error{ PayloadTooLarge, Io, NoSpace }

NoSpace: the filesystem is full (or over quota) → 507, retryable.

server.ChunkWriter.Encoding

type · línea 1428

pub const Encoding = enum

Sin ///.

server.ChunkWriter.PumpError

const · línea 1430

pub const PumpError = error{ /// The connection failed while reading the body: `cancel` and drop it. ReadFailed, /// The writer answered; its status is returned by `failPump`. Answered, }

Sin ///.

server.ChunkWriter.Hash

type · línea 1437

pub const Hash = union

Sin ///.

server.ChunkWriter.remaining

fn · línea 1443

pub fn remaining(w: *const ChunkWriter) u64

Bytes still expected; bindings read at most this much.

server.ChunkWriter.write

fn · línea 1447

pub fn write(w: *ChunkWriter, bytes: []const u8) WriteError!void

Sin ///.

server.ChunkWriter.pumpZstd

fn · línea 1471

pub fn pumpZstd(w: *ChunkWriter, in: *std.Io.Reader, out: *Writer) PumpError!void

Decode a Content-Encoding: zstd body from in (exactly the encoded body) into the chunk, then the caller calls finish. The encoded bytes are bounded by the range length here, not only by a Content-Length (a chunked body has none): one byte more is a 400. On error.Answered the response is already in out with status w.answered; on error.ReadFailed the caller cancels.

server.ChunkWriter.finish

fn · línea 1502

pub fn finish(w: *ChunkWriter, out: *Writer) u16

The body ended. Verifies length and digest, stores the chunk and advances the file hash. Writes the response JSON; returns the status.

server.ChunkWriter.cancel

fn · línea 1576

pub fn cancel(w: *ChunkWriter) void

Connection dropped or the binding gave up: the chunk is discarded.

server.ChunkWriter.fail

fn · línea 1595

pub fn fail(w: *ChunkWriter, err: WriteError, out: *Writer) u16

write failed: discard the chunk and answer with the matching error.

server.removeOrphanStaging

fn · línea 1698

pub fn removeOrphanStaging(root: *const Root) u32

Unlink every staging file (<32 lowercase hex>.part) directly under root, fd-relative: a symlink with that name is unlinked, never followed, and nothing else is touched (final files are named by their SHA-256, without .part). Returns how many were removed. Only safe while no server is using root (Server.init calls it before any session).

server.isStagingName

fn · línea 1715

pub fn isStagingName(name: []const u8) bool

<32 lowercase hex>.part, the only name create gives a staging file.