silt

silt

Byte-exact git object ids,
in pure Gleam.

silt turns content into content-addressed blob, tree, and commit objects whose SHA-1 ids match real git — exactly. It also shapes them into the responses for a git-compatible content-addressable storage REST API. Pure logic; it owns no store.

reproduce it → printf 'blob 5\0hello' | sha1sum

object_id("blobs", …) git-canonical
  1. content "hello"
  2. encoding utf-8
  3. frame blob 5\0
  4. bytes 68 65 6c 6c 6f
  5. sha-1 compress
sha-1 b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0

Content settles into a name.

silt names an object by its contents. Hash the bytes, get an id; the same bytes always produce the same id — so the id is both the object's name and a check on its integrity. Nothing is addressed by where it lives, only by what it holds.

That is the whole discipline of content-addressed storage, and it only holds if every stack computes the id the same way, down to the byte. silt exists to be that one exact computation — shared, not re-derived.


Two guarantees hold everything up.

Ids that match real git

Blobs are framed the way git frames them — blob <length>\0<content> — then hashed with SHA-1, so object_id("blobs", …) equals what git hash-object gives you. Trees and commits hash their serialized body directly. Change nothing about the framing and every stack agrees on every id.

git-canonical id
import silt

// git-canonical SHA-1 — matches `git hash-object`
let assert Ok(sha) =
  silt.object_id("blobs", "{\"content\":\"hello\"}")
// -> "b6fc4c62…5db5a1b0"

No store, by design

silt is pure logic over content. It holds nothing, persists nothing, and never reaches for a database. When a response has to walk child objects — a recursive tree, a line of commit history — you hand it a fetch that closes over your store. silt shapes; you keep the bytes.

caller-supplied fetch
// Walk children (recursive trees, history) via a
// fetch that closes over your own store.
let fetch = fn(sha) { my_store.get(tenant, sha) }

let response =
  silt.object_response(
    base_url, tenant, "trees", sha, body, True, fetch,
  )

The surface, in three moves.

Compute an id, decode a stored body into a typed value, shape it for the REST API. Full signatures live on the reference pages.

silt in three calls
import silt
import silt/object
import silt/rest

// 1 · content -> id
let assert Ok(sha) = silt.object_id("commits", body)

// 2 · read a stored body back into a typed value
let assert Ok(commit) = object.decode_commit(body)

// 3 · shape it for the REST API
let assert Ok(json) =
  rest.object_response(base_url, tenant, "commits", sha, body, False, fetch)

Add it to your project.

silt ships as a pinned git dependency — it isn't published to Hex. Point gleam.toml at the repository and a ref:

  • Pin ref to a commit or tag for reproducible builds.
  • gleam add doesn't manage git dependencies — edit gleam.toml, then run gleam deps download.
  • Runs on both Gleam targets — ids are byte-identical on Erlang and JavaScript. Pure Gleam, no FFI.
gleam.toml
[dependencies]
silt = { git = "https://github.com/tylerbutler/silt", ref = "main" }