~/side $ ./sakura --selftest // 47/47 ok

SAKURA

a simple, fast and small Blossom server, written in Odin

~1.2 MB static binary 0 dependencies FROM scratch docker image 47 self-test checks

[01] WHAT IT IS

Relays move your notes. The images, video and files behind them need a home too: that's what a Blossom server is.

Sakura is that server: it stores your media addressed by its SHA-256 hash, serves it to any client that asks, and lets only your nostr keys upload or delete. Small enough to run on whatever box you own, so your files live with you and survive any platform.

the implemented BUDs, covering the Blossom spec end to end:

[02] ENGINEERING

lock-free reads

GET/HEAD stat and stream blobs straight off disk in 64 KiB chunks; the index mutex is only taken for mutations and metadata.

one worker per core

All worker threads accept on a single shared listening socket, with HTTP keep-alive and request pipelining.

zero heap churn

Each request runs on an arena allocator that's reset afterwards, so parsing and response building allocate nothing per-request.

hand-rolled crypto

Custom secp256k1 with 4×u64 limbs and u128 intermediates, exploiting the shape of p = 2²⁵⁶ − 2³² − 977. Verification is variable-time on purpose: it only touches public data.

streaming SHA-256

Hashes directly out of the upload buffer, with no staging copies.

tested like it matters

47 in-binary known-answer checks: NIST SHA-256 vectors, all 19 BIP-340 spec vectors including every must-fail edge case, plus differential and live end-to-end suites.

[03] RUN IT

# build (or skip straight to docker)
$ odin build . -out:sakura -o:speed

# run
$ SAKURA_PORT=3000 SAKURA_DATA=./data ./sakura

# docker: FROM scratch. no OS, no libc, no shell.
# image is ~1.7 MB and runs the selftest at build time.
$ docker build -t sakura .

# or pull the prebuilt image
$ docker pull ghcr.io/arx-ccn/sakura:latest

$ docker run --rm -p 3000:3000 -v sakura-data:/data sakura

configuration: env vars

SAKURA_HOST0.0.0.0bind address
SAKURA_PORT3000listen port
SAKURA_DATA./datablob + index dir
SAKURA_DOMAIN(off)BUD-11 server-tag scope
SAKURA_MAX_MB100max blob size
SAKURA_WORKERS16worker threads
SAKURA_REQUIRE_AUTHtrueauth for upload/delete/mirror

[04] SOURCE

ten files, zero dependencies

  • sha256.odinstreaming SHA-256
  • secp256k1.odinfield/scalar/point math, BIP-340
  • codec.odinhex, Base64-URL
  • json.odinJSON parser + NIP-01 escaping
  • nostr.odinevents, BUD-11 validation, tokens
  • store.odinsharded blob store + index
  • http.odinrequest parser, streaming writer
  • http_client.odinminimal client for /mirror
  • server.odinrouting, every endpoint
  • main.odinconfig, workers, selftest, CLI

honest scope

/mirror fetches http:// sources only; TLS is out of scope for a dependency-free build. BUD-05 (media optimization) and BUD-07 (payments) are intentionally not implemented. Alpha software; expect rough petals.

READ THE CODE ⇗