~/side $ ./sakura --selftest // 47/47 ok
SAKURA
a simple, fast and small Blossom server, written in Odin
[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:
GET/HEAD /<sha256> with RFC 7233 range requests, full CORS, content sniffing.
BUD-02
upload
PUT /upload: blob descriptors, 200/201 semantics, X-SHA-256 integrity check.
BUD-04
mirror
PUT /mirror: fetch from http sources, hash-verify, store.
BUD-06
probe
HEAD /upload upload-requirements probe before sending a byte.
BUD-11
auth
kind-24242 nostr tokens: event id recomputed, Schnorr signature verified, every tag checked.
BUD-12
list / delete
GET /list/<pubkey> and DELETE /<sha256>.
[02] ENGINEERING
GET/HEAD stat and stream blobs straight off disk in 64 KiB chunks; the index mutex is only taken for mutations and metadata.
All worker threads accept on a single shared listening socket, with HTTP keep-alive and request pipelining.
Each request runs on an arena allocator that's reset afterwards, so parsing and response building allocate nothing per-request.
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.
Hashes directly out of the upload buffer, with no staging copies.
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_HOST | 0.0.0.0 | bind address |
| SAKURA_PORT | 3000 | listen port |
| SAKURA_DATA | ./data | blob + index dir |
| SAKURA_DOMAIN | (off) | BUD-11 server-tag scope |
| SAKURA_MAX_MB | 100 | max blob size |
| SAKURA_WORKERS | 16 | worker threads |
| SAKURA_REQUIRE_AUTH | true | auth for upload/delete/mirror |
[04] SOURCE
ten files, zero dependencies
sha256.odinstreaming SHA-256secp256k1.odinfield/scalar/point math, BIP-340codec.odinhex, Base64-URLjson.odinJSON parser + NIP-01 escapingnostr.odinevents, BUD-11 validation, tokensstore.odinsharded blob store + indexhttp.odinrequest parser, streaming writerhttp_client.odinminimal client for /mirrorserver.odinrouting, every endpointmain.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.