All services

bytes · operations surface

Instance bytes.core.bitnode.dev

Contract 2026-09-10 · build a4c323cd37ae1fce1ae5c4ad4488c86f29075bc6 · go1.26.3 · generated 2026-09-17T22:31:36Z from https://bytes.core.bitnode.dev/api/operations/describe

Bindings

Primaryhttp
HTTPprefix /v1

Operations

NameKindBindingRequestResponseParametersRefusals
getread GET /v1/blobs/{name} bytes name: string (required)not_found
get_rangeread GET /v1/blobs/{name} bytes name: string (required), offset: int, length: intnot_found
get_streamstream GET /v1/blobs/{name} bytes name: string (required)not_found
hasread POST /v1/stat StatRequest StatResponse
sizeread POST /v1/stat StatRequest StatResponse
putmutation POST /v1/blobs bytes PutResponse too_large
put_streammutation POST /v1/blobs bytes PutResponse too_large
get_manyread POST /v1/get NamesRequest records
put_manymutation POST /v1/put records PutManyResponse
livezoperational GET /livez Health
healthzoperational GET /healthz Health
describeoperational GET /describe Describe
describe_fileoperational GET /describe/{file...} file file: string (required)not_found

Refusals

CodeHTTPMeaning
not_found404the name asked for is not here
too_large413the bytes exceed what this deployment accepts
bad_request400a malformed name or request body: a protocol error of this binding, not a refusal of content

Files served

Each one verbatim, byte for byte what the listener served at /describe/{file}.

SURFACE.md

The surface

Contract: 2026-09-10, the day this surface last changed; served as Ulab-Contract on every response and carried by the SDK as bnbytes.Contract.

Nine verbs, two refusals, one name grammar. This file is the contract; the bindings (see HTTP.md) carry it, they do not extend it. The operational routes of ulab-standard/OPERATIONS.md (/livez, /healthz, /describe) are not part of it; the binding document states them.

Names

sha256:<64 lowercase hex digits>

The digest of the bytes, prefixed by the algorithm so a future algorithm is visible rather than silent. Names are compared as strings; there is no canonicalization beyond the grammar itself. A string that does not match the grammar is not a name — rejecting it is a protocol error of the binding, not a refusal of content, so it does not appear in the refusal list.

Verbs

verb in out
get name the bytes
get many names blobs, keyed by name, unordered
has names which of them are here
size names byte count per name that is here
get range name, offset, length the overlapping bytes
get stream name the bytes, incrementally
put bytes name
put many list of bytes names, per item
put stream bytes, incrementally name

has and size are plural. Singular existence-checking pays one round trip per node, and the client that asks is usually deciding fetch-whole versus stream for a set of children it just discovered, not for one blob. A plural size quietly subsumes has — a name with a size is here, a name without one is not — so a binding may carry both verbs on one operation. The singular case is a set of one.

Pinned behaviors

These were once open questions (#3); they are now contract. None of them changes what the service is — which held only under these answers, and is why they were decided before an implementation could decide them by accident.

put stream commits atomically, at clean end-of-stream, under the hash of exactly what arrived. Integrity under the full content's hash cannot be violated — the server only knows the hash of what it received, and those bytes self-verify. What this clause forbids is prefix-commit: an aborted stream's prefix also self-verifies, so committing it under hash(prefix) breaks no naming identity, yet a partial object becomes visible under a hash. Therefore: any abort — client disconnect, or the server tripping its deployment cap mid-stream — commits nothing. Dedup at end-of-stream is the ordinary idempotent put; note that this short-circuit is necessarily server-side and commit-time-only, unlike the has-before-put skip, which is the SDK's.

put many is per-item, not atomic across the batch. Batch atomicity would falsify the README's "a crashed top-down put many leaves [a dangling node]", and the leaves-first write convention exists precisely because batches land partially. Retries re-put, dedup absorbs them, residue falls under "GC must be driven by whoever holds the roots." An oversized item refuses alone; its neighbors land.

get range past the end is a short read. Refusal would be legal — size is structural, not semantic — but the refusal enumeration decides it: a range refusal would grow the list of two. The store returns the overlap of the requested range with the blob; a range entirely past the end overlaps nothing and returns zero bytes. A range is the only read verify-on-read cannot check; immutability makes size a reliable clamp, and a client that needs verification fetches whole or streams.

get many is unordered, keyed by name. Ordering is most of what the plural form would otherwise cost; keying is free, since verify-on-read hashes every blob anyway, and it handles duplicate names and per-item absence — a name that is not here is simply absent from the result, not an error.

get stream cancellation is nothing, server-side. The client stops reading; the server stops sending. No state, no verb, no cleanup protocol.

Refusals

Only two, neither about content:

refusal meaning
too large the bytes exceed what this deployment accepts
not found the name asked for is not here

There is no "malformed" — that is a judgement about a format, and there is no format here to judge against.

not found is a refusal only where a single blob's bytes were demanded (get, get range, get stream). In the plural verbs absence is data — an omitted key — because a batch that fails on its most absent member is useless for the negotiation the plural verbs exist to serve.

Persistence, cancellation and uncertain outcomes

An acknowledged put, put stream or put many item is durable before it is acknowledged: the bytes are written and flushed to their file, the file is published under its name by one rename, and the containing directories are flushed where the platform allows it. The blob survives a process crash and a restart with the same data directory. Power-loss durability is bounded by the filesystem and device honouring those flushes; the evidence behind this paragraph is process-kill and restart tests, not power-loss tests.

Reads never return bytes that were not committed: a name is either absent or names the complete blob. A crash mid-stream leaves at most a temporary file that no verb can reach, never a partial object under a name.

A caller that cancels, times out or loses the connection after sending bytes learns nothing about the outcome: the request either committed the whole blob or committed nothing, never part of one. Because names are content-derived and put is idempotent, the caller resolves the uncertainty by checking the expected name with has or by sending the same bytes again; both are safe. An acknowledgement can be lost after a commit, so an error after dispatch is not proof that the blob is absent. Cancelling a get stream is nothing server-side; cancelling a put stream commits nothing.

What the SDK adds, off the wire

Verify-on-read, cache tiers, skipping a put when has already says yes, parallel fetch, failover across mirrors. These live in the client so that the storage does not have to be trusted — only available. Nothing on the wire knows about any of them.

HTTP.md

The HTTP binding

One binding of the surface. It carries the nine verbs on five routes; it adds nothing.

Application routes live under /v1. The version belongs to this binding, not to the service: the contract's worth is that it does not change, and the prefix is what lets that claim survive being wrong. The service's name is bytes, with no version, and every response carries two headers:

Ulab-Service: bytes
Ulab-Contract: 2026-09-10

Ulab-Contract is the date on the surface's contract line. A client treats a response without Ulab-Service: bytes as a protocol error, before reading its body, whatever its status.

Routes

route verbs
GET /v1/blobs/{name} get, get range, get stream
POST /v1/stat has, size
POST /v1/blobs put, put stream
POST /v1/get get many
POST /v1/put put many

GET /v1/blobs/{name} — get, get range, get stream

Returns the bytes, 200, Content-Type: application/octet-stream, with Content-Length when known. Whether the client treats the body as a whole blob or a stream is the client's business; the wire is identical.

Query parameters offset and length (non-negative integers) select a range. Per the surface, the response is the overlap: length is clamped to the end, an offset at or past the end yields an empty 200. The HTTP Range header is not part of this binding — its unsatisfiable-range semantics (416) would smuggle in a third refusal.

Cancellation is the client closing the connection. The server sends nothing further and keeps nothing.

Errors: 404 if the name is not here.

POST /v1/stat — has, size

Request application/json:

{"names": ["sha256:…", "sha256:…"]}

Response 200 application/json:

{"sizes": {"sha256:…": 1024}}

sizes holds an entry for every requested name that is here, and no entry for one that is not. This one operation is both plural verbs: presence of the key is has, its value is size.

POST /v1/blobs — put, put stream

The request body is the bytes, raw. The server hashes what arrives and commits at clean end-of-body — for a streamed (chunked) request this is the atomic commit the surface pins: an incomplete body, whoever aborted it, commits nothing. Content already present short-circuits at commit time; the response does not distinguish it, because the verb is idempotent and the distinction is not the client's business.

Response 200 application/json:

{"name": "sha256:…", "size": 1024}

Errors: 413 if the bytes exceed the deployment cap — including when the cap trips mid-stream, in which case nothing was committed.

POST /v1/get — get many

Request application/json:

{"names": ["sha256:…", "sha256:…"]}

Response 200 application/x-bn-blobs: a sequence of records, one per blob that is here, in any order. Each record is an ASCII header line

<name> <size>\n

followed by exactly <size> raw bytes. The sequence ends at end-of-body. Requested names that are not here produce no record. Duplicate requested names produce one record.

POST /v1/put — put many

Request application/x-bn-blobs with anonymous records — the header line is just <size>\n, since the name is what the server is being asked to compute — each followed by exactly <size> raw bytes.

Response 200 application/json, one entry per record, in request order (the items have no names until now, so order is the only key there is):

{"results": [
  {"name": "sha256:…", "size": 3},
  {"error": "too_large", "message": "the body exceeds the per-blob limit"}
]}

Each item commits independently, per the surface: an oversized item refuses alone, its neighbors land, and a batch cut off mid-body commits the records that arrived whole and nothing of the one that did not.

Operational routes

Four routes at the root, per ulab-standard/OPERATIONS.md, without credentials and outside the surface. They are rows of kind operational in operations.json.

route answers status
GET /livez the process is up 200 always
GET /healthz the store can serve: the data directory exists and a temporary file can be created where commits stage 200, or 503 with the failing check
GET /describe the service's description: name, contract date, build, bindings, the operation table, the refusal table, the served files 200
GET /describe/{file} one committed contract file, verbatim, by a name describe.files lists; {file} spans slashes for schema/<Type>.schema.json 200, 404 not_found otherwise

/livez and /healthz answer the health envelope:

{"status":"ok","service":"bytes","contract":"2026-09-10",
 "build":{"revision":"<vcs revision>","dirty":false,"go":"go1.26.3"},
 "time":"2026-09-10T02:10:00Z",
 "checks":[{"name":"store","status":"ok","detail":"data directory writable"}]}

/livez carries an empty checks array. build comes from the binary's build information, or from the link-time variables github.com/Bitspark/bn-bytes/impl/cloud/go/api.Revision and .Dirty when the build carried none (-buildvcs=false), or is "unknown" and true. A bytes store has no instance identity, so the envelope and the description carry no identity member.

/describe serves the files embedded in the binary at build: SURFACE.md, HTTP.md, operations.json, openapi.json and schema/<Type>.schema.json, each verbatim as text/markdown or application/json. A drift test holds the embedded files byte-equal to the committed ones.

Validation

Since 2026-09-10 the binding refuses what it does not document, per the family's validation rule: on GET /v1/blobs/{name} any query parameter other than offset and length, a repeated one, or an empty one is 400 bad_request; a JSON body for /v1/stat or /v1/get must be one object whose only member is names, given once, followed by nothing but whitespace, and any unknown or repeated member, trailing data or non-array names is 400 bad_request. JSON bodies are limited to 4 MiB. A client that misspells a parameter hears about it instead of receiving the whole blob. The Go router's own 404 and 405 answers and its canonical-path redirects are outcomes of this binding, not refusals of content; a validated SDK refuses redirects and is configured with the final endpoint. Batch responses are buffered in proportion to the item count; there is no item-count cap.

Errors

Error responses carry application/json:

{"error": "not_found", "message": "no such object or route"}
status error where
404 not_found GET /v1/blobs/{name}
413 too_large POST /v1/blobs, per-item in POST /v1/put
400 bad_request a malformed name or request body — a protocol error of this binding, not a refusal of content

Nothing else is a refusal. 5xx means the server failed, not that it refused; a client retries or fails over, which content addressing makes safe.

Schemas

schema/Describe.schema.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$defs": {
    "Bindings": {
      "properties": {
        "primary": {
          "type": "string"
        },
        "http": {
          "$ref": "#/$defs/HTTPPrefix"
        }
      },
      "type": "object",
      "required": [
        "primary",
        "http"
      ],
      "description": "Bindings names the primary binding and the base of each one offered."
    },
    "Build": {
      "properties": {
        "revision": {
          "type": "string"
        },
        "dirty": {
          "type": "boolean"
        },
        "go": {
          "type": "string"
        }
      },
      "type": "object",
      "required": [
        "revision",
        "dirty",
        "go"
      ],
      "description": "Build identifies the binary that answered: the version-control revision it was built from and whether that tree was dirty, from the binary's build information or from values stamped at link time, else \"unknown\" and true."
    },
    "HTTPBinding": {
      "properties": {
        "method": {
          "type": "string"
        },
        "path": {
          "type": "string"
        }
      },
      "type": "object",
      "required": [
        "method",
        "path"
      ],
      "description": "HTTPBinding carries an operation as one request and one response."
    },
    "HTTPPrefix": {
      "properties": {
        "prefix": {
          "type": "string"
        }
      },
      "type": "object",
      "required": [
        "prefix"
      ],
      "description": "HTTPPrefix is where the application routes of the HTTP binding live."
    },
    "Operation": {
      "properties": {
        "name": {
          "type": "string"
        },
        "kind": {
          "type": "string"
        },
        "doc": {
          "type": "string"
        },
        "params": {
          "items": {
            "$ref": "#/$defs/Param"
          },
          "type": "array"
        },
        "request": {
          "type": "string",
          "description": "body type; \"bytes\" or \"records\" for raw bodies"
        },
        "response": {
          "type": "string",
          "description": "result type; \"bytes\" or \"records\" for raw bodies"
        },
        "errors": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "description": "refusals beyond bad_request"
        },
        "http": {
          "$ref": "#/$defs/HTTPBinding"
        }
      },
      "type": "object",
      "required": [
        "name",
        "kind",
        "doc",
        "params",
        "errors",
        "http"
      ],
      "description": "Operation is one row of api/operations.json."
    },
    "Param": {
      "properties": {
        "name": {
          "type": "string"
        },
        "kind": {
          "type": "string"
        },
        "required": {
          "type": "boolean"
        },
        "doc": {
          "type": "string"
        }
      },
      "type": "object",
      "required": [
        "name",
        "kind",
        "required",
        "doc"
      ],
      "description": "Param is one named input of an operation, outside its body."
    },
    "Refusal": {
      "properties": {
        "name": {
          "type": "string"
        },
        "http": {
          "type": "integer"
        },
        "meaning": {
          "type": "string"
        }
      },
      "type": "object",
      "required": [
        "name",
        "http",
        "meaning"
      ],
      "description": "Refusal is one row of the refusal table: a name the surface defines, the HTTP status the binding maps it to, and its meaning."
    }
  },
  "properties": {
    "service": {
      "type": "string"
    },
    "contract": {
      "type": "string"
    },
    "build": {
      "$ref": "#/$defs/Build"
    },
    "bindings": {
      "$ref": "#/$defs/Bindings"
    },
    "operations": {
      "items": {
        "$ref": "#/$defs/Operation"
      },
      "type": "array"
    },
    "refusals": {
      "items": {
        "$ref": "#/$defs/Refusal"
      },
      "type": "array"
    },
    "files": {
      "items": {
        "type": "string"
      },
      "type": "array"
    },
    "identity": {
      "type": "object"
    }
  },
  "type": "object",
  "required": [
    "service",
    "contract",
    "build",
    "bindings",
    "operations",
    "refusals",
    "files"
  ],
  "title": "Describe",
  "description": "Describe is the document /describe returns."
}
schema/Error.schema.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "error": {
      "type": "string"
    }
  },
  "type": "object",
  "required": [
    "error"
  ],
  "title": "Error",
  "description": "ErrorResponse is the JSON envelope of every refusal and protocol error."
}
schema/Health.schema.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$defs": {
    "Build": {
      "properties": {
        "revision": {
          "type": "string"
        },
        "dirty": {
          "type": "boolean"
        },
        "go": {
          "type": "string"
        }
      },
      "type": "object",
      "required": [
        "revision",
        "dirty",
        "go"
      ],
      "description": "Build identifies the binary that answered: the version-control revision it was built from and whether that tree was dirty, from the binary's build information or from values stamped at link time, else \"unknown\" and true."
    },
    "Check": {
      "properties": {
        "name": {
          "type": "string"
        },
        "status": {
          "type": "string",
          "description": "ok or failing"
        },
        "detail": {
          "type": "string"
        }
      },
      "type": "object",
      "required": [
        "name",
        "status",
        "detail"
      ],
      "description": "Check is one dependency the service needs in order to serve."
    }
  },
  "properties": {
    "status": {
      "type": "string"
    },
    "service": {
      "type": "string"
    },
    "contract": {
      "type": "string"
    },
    "build": {
      "$ref": "#/$defs/Build"
    },
    "time": {
      "type": "string"
    },
    "checks": {
      "items": {
        "$ref": "#/$defs/Check"
      },
      "type": "array"
    },
    "identity": {
      "type": "object"
    }
  },
  "type": "object",
  "required": [
    "status",
    "service",
    "contract",
    "build",
    "time",
    "checks"
  ],
  "title": "Health",
  "description": "Health is the envelope of /livez and /healthz."
}
schema/NamesRequest.schema.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "names": {
      "items": {
        "type": "string"
      },
      "type": "array"
    }
  },
  "type": "object",
  "required": [
    "names"
  ],
  "title": "NamesRequest",
  "description": "NamesRequest is the body of get many."
}
schema/PutManyResponse.schema.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$defs": {
    "PutManyItem": {
      "properties": {
        "name": {
          "type": "string"
        },
        "size": {
          "type": "integer"
        },
        "error": {
          "type": "string"
        }
      },
      "type": "object",
      "description": "PutManyItem is one entry of a put many response: a name and size, or an error for the item that refused alone."
    }
  },
  "properties": {
    "results": {
      "items": {
        "$ref": "#/$defs/PutManyItem"
      },
      "type": "array"
    }
  },
  "type": "object",
  "required": [
    "results"
  ],
  "title": "PutManyResponse",
  "description": "PutManyResponse answers put many, one entry per record in request order."
}
schema/PutResponse.schema.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "name": {
      "type": "string"
    },
    "size": {
      "type": "integer"
    }
  },
  "type": "object",
  "required": [
    "name",
    "size"
  ],
  "title": "PutResponse",
  "description": "PutResponse acknowledges put and put stream."
}
schema/StatRequest.schema.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "names": {
      "items": {
        "type": "string"
      },
      "type": "array"
    }
  },
  "type": "object",
  "required": [
    "names"
  ],
  "title": "StatRequest",
  "description": "StatRequest and NamesRequest are the JSON bodies of /v1/stat and /v1/get."
}
schema/StatResponse.schema.json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "sizes": {
      "additionalProperties": {
        "type": "integer"
      },
      "type": "object"
    }
  },
  "type": "object",
  "required": [
    "sizes"
  ],
  "title": "StatResponse",
  "description": "StatResponse answers has and size: an entry for every requested name that is here, none for one that is not."
}

OpenAPI

openapi.json (raw)
{
  "components": {
    "schemas": {
      "Bindings": {
        "description": "Bindings names the primary binding and the base of each one offered.",
        "properties": {
          "http": {
            "$ref": "#/components/schemas/HTTPPrefix"
          },
          "primary": {
            "type": "string"
          }
        },
        "required": [
          "primary",
          "http"
        ],
        "type": "object"
      },
      "Build": {
        "description": "Build identifies the binary that answered: the version-control revision it was built from and whether that tree was dirty, from the binary's build information or from values stamped at link time, else \"unknown\" and true.",
        "properties": {
          "dirty": {
            "type": "boolean"
          },
          "go": {
            "type": "string"
          },
          "revision": {
            "type": "string"
          }
        },
        "required": [
          "revision",
          "dirty",
          "go"
        ],
        "type": "object"
      },
      "Check": {
        "description": "Check is one dependency the service needs in order to serve.",
        "properties": {
          "detail": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "description": "ok or failing",
            "type": "string"
          }
        },
        "required": [
          "name",
          "status",
          "detail"
        ],
        "type": "object"
      },
      "Describe": {
        "description": "Describe is the document /describe returns.",
        "properties": {
          "bindings": {
            "$ref": "#/components/schemas/Bindings"
          },
          "build": {
            "$ref": "#/components/schemas/Build"
          },
          "contract": {
            "type": "string"
          },
          "files": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "identity": {
            "type": "object"
          },
          "operations": {
            "items": {
              "$ref": "#/components/schemas/Operation"
            },
            "type": "array"
          },
          "refusals": {
            "items": {
              "$ref": "#/components/schemas/Refusal"
            },
            "type": "array"
          },
          "service": {
            "type": "string"
          }
        },
        "required": [
          "service",
          "contract",
          "build",
          "bindings",
          "operations",
          "refusals",
          "files"
        ],
        "title": "Describe",
        "type": "object"
      },
      "Error": {
        "description": "ErrorResponse is the JSON envelope of every refusal and protocol error.",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ],
        "title": "Error",
        "type": "object"
      },
      "HTTPBinding": {
        "description": "HTTPBinding carries an operation as one request and one response.",
        "properties": {
          "method": {
            "type": "string"
          },
          "path": {
            "type": "string"
          }
        },
        "required": [
          "method",
          "path"
        ],
        "type": "object"
      },
      "HTTPPrefix": {
        "description": "HTTPPrefix is where the application routes of the HTTP binding live.",
        "properties": {
          "prefix": {
            "type": "string"
          }
        },
        "required": [
          "prefix"
        ],
        "type": "object"
      },
      "Health": {
        "description": "Health is the envelope of /livez and /healthz.",
        "properties": {
          "build": {
            "$ref": "#/components/schemas/Build"
          },
          "checks": {
            "items": {
              "$ref": "#/components/schemas/Check"
            },
            "type": "array"
          },
          "contract": {
            "type": "string"
          },
          "identity": {
            "type": "object"
          },
          "service": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "time": {
            "type": "string"
          }
        },
        "required": [
          "status",
          "service",
          "contract",
          "build",
          "time",
          "checks"
        ],
        "title": "Health",
        "type": "object"
      },
      "NamesRequest": {
        "description": "NamesRequest is the body of get many.",
        "properties": {
          "names": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "names"
        ],
        "title": "NamesRequest",
        "type": "object"
      },
      "Operation": {
        "description": "Operation is one row of api/operations.json.",
        "properties": {
          "doc": {
            "type": "string"
          },
          "errors": {
            "description": "refusals beyond bad_request",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "http": {
            "$ref": "#/components/schemas/HTTPBinding"
          },
          "kind": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "params": {
            "items": {
              "$ref": "#/components/schemas/Param"
            },
            "type": "array"
          },
          "request": {
            "description": "body type; \"bytes\" or \"records\" for raw bodies",
            "type": "string"
          },
          "response": {
            "description": "result type; \"bytes\" or \"records\" for raw bodies",
            "type": "string"
          }
        },
        "required": [
          "name",
          "kind",
          "doc",
          "params",
          "errors",
          "http"
        ],
        "type": "object"
      },
      "Param": {
        "description": "Param is one named input of an operation, outside its body.",
        "properties": {
          "doc": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "required": {
            "type": "boolean"
          }
        },
        "required": [
          "name",
          "kind",
          "required",
          "doc"
        ],
        "type": "object"
      },
      "PutManyItem": {
        "description": "PutManyItem is one entry of a put many response: a name and size, or an error for the item that refused alone.",
        "properties": {
          "error": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          }
        },
        "type": "object"
      },
      "PutManyResponse": {
        "description": "PutManyResponse answers put many, one entry per record in request order.",
        "properties": {
          "results": {
            "items": {
              "$ref": "#/components/schemas/PutManyItem"
            },
            "type": "array"
          }
        },
        "required": [
          "results"
        ],
        "title": "PutManyResponse",
        "type": "object"
      },
      "PutResponse": {
        "description": "PutResponse acknowledges put and put stream.",
        "properties": {
          "name": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          }
        },
        "required": [
          "name",
          "size"
        ],
        "title": "PutResponse",
        "type": "object"
      },
      "Refusal": {
        "description": "Refusal is one row of the refusal table: a name the surface defines, the HTTP status the binding maps it to, and its meaning.",
        "properties": {
          "http": {
            "type": "integer"
          },
          "meaning": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "name",
          "http",
          "meaning"
        ],
        "type": "object"
      },
      "StatRequest": {
        "description": "StatRequest and NamesRequest are the JSON bodies of /v1/stat and /v1/get.",
        "properties": {
          "names": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "names"
        ],
        "title": "StatRequest",
        "type": "object"
      },
      "StatResponse": {
        "description": "StatResponse answers has and size: an entry for every requested name that is here, none for one that is not.",
        "properties": {
          "sizes": {
            "additionalProperties": {
              "type": "integer"
            },
            "type": "object"
          }
        },
        "required": [
          "sizes"
        ],
        "title": "StatResponse",
        "type": "object"
      }
    }
  },
  "info": {
    "description": "Generated from bnbytes.Operations and the SDK's types (ulab-standard/API.md). The contract is SURFACE.md and HTTP.md: record framing, atomic commit at end of stream, per-item batch commit and short reads live there and not here. Verbs that share a route appear once, with every verb listed under x-ulab-verbs.",
    "title": "bytes",
    "version": "2026-09-10"
  },
  "openapi": "3.1.0",
  "paths": {
    "/describe": {
      "get": {
        "operationId": "describe",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Describe"
                }
              }
            },
            "description": "the result"
          }
        },
        "summary": "who this is, which contract, which build, which bindings, which operations, which refusals",
        "tags": [
          "operational"
        ],
        "x-ulab-verbs": [
          "describe"
        ]
      }
    },
    "/describe/{file}": {
      "get": {
        "operationId": "describe_file",
        "parameters": [
          {
            "description": "a name listed by describe.files",
            "in": "path",
            "name": "file",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/markdown or application/json, per file": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "the result"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "not_found"
          }
        },
        "summary": "one committed contract file, verbatim",
        "tags": [
          "operational"
        ],
        "x-ulab-verbs": [
          "describe_file"
        ]
      }
    },
    "/healthz": {
      "get": {
        "operationId": "healthz",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            },
            "description": "the result"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            },
            "description": "failing: the same envelope with the failing checks"
          }
        },
        "summary": "the service can serve its contract: every dependency check passes",
        "tags": [
          "operational"
        ],
        "x-ulab-verbs": [
          "healthz"
        ]
      }
    },
    "/livez": {
      "get": {
        "operationId": "livez",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            },
            "description": "the result"
          }
        },
        "summary": "the process is up and can answer",
        "tags": [
          "operational"
        ],
        "x-ulab-verbs": [
          "livez"
        ]
      }
    },
    "/v1/blobs": {
      "post": {
        "operationId": "put",
        "requestBody": {
          "content": {
            "application/octet-stream": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PutResponse"
                }
              }
            },
            "description": "the result"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "bad_request"
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "too_large"
          }
        },
        "summary": "store bytes under their hash; idempotent",
        "tags": [
          "mutation"
        ],
        "x-ulab-verbs": [
          "put",
          "put_stream"
        ]
      }
    },
    "/v1/blobs/{name}": {
      "get": {
        "operationId": "get",
        "parameters": [
          {
            "description": "sha256:\u003c64 lowercase hex\u003e",
            "in": "path",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "first byte, default 0",
            "in": "query",
            "name": "offset",
            "required": false,
            "schema": {
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "byte count, default to the end",
            "in": "query",
            "name": "length",
            "required": false,
            "schema": {
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/octet-stream": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "the result"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "bad_request"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "not_found"
          }
        },
        "summary": "the bytes of one blob",
        "tags": [
          "read"
        ],
        "x-ulab-verbs": [
          "get",
          "get_range",
          "get_stream"
        ]
      }
    },
    "/v1/get": {
      "post": {
        "operationId": "get_many",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NamesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/x-bn-blobs": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "the result"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "bad_request"
          }
        },
        "summary": "the blobs that are here among the names, as records keyed by name, unordered",
        "tags": [
          "read"
        ],
        "x-ulab-verbs": [
          "get_many"
        ]
      }
    },
    "/v1/put": {
      "post": {
        "operationId": "put_many",
        "requestBody": {
          "content": {
            "application/x-bn-blobs": {
              "schema": {
                "format": "binary",
                "type": "string"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PutManyResponse"
                }
              }
            },
            "description": "the result"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "bad_request"
          }
        },
        "summary": "store each anonymous record independently; results in request order",
        "tags": [
          "mutation"
        ],
        "x-ulab-verbs": [
          "put_many"
        ]
      }
    },
    "/v1/stat": {
      "post": {
        "operationId": "has",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StatRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatResponse"
                }
              }
            },
            "description": "the result"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "bad_request"
          }
        },
        "summary": "which of the names are here: presence of a key in sizes",
        "tags": [
          "read"
        ],
        "x-ulab-verbs": [
          "has",
          "size"
        ]
      }
    }
  },
  "servers": [
    {
      "description": "the standalone default listener",
      "url": "http://127.0.0.1:8080"
    }
  ]
}