Skip to main content

swarmlite

tests PyPI license

Verifiable serverless SQLite hosting on Ethereum Swarm.

Publish an ordinary SQLite file to Swarm and run SELECT against it — fetching only the B-tree pages the query plan touches, each one verifiable against the file's 32-byte content address. No database server anywhere.

Measured live (Bee 2.8.1 light node, Gnosis mainnet, 134.5 MB database):

$ swarmlite query "bzz://<root>/demo.db" \
      "SELECT title FROM posts WHERE id = 73123" --stats
Post 73123: on sqlite
fetched 5 pages (20 KB) in 5 reads, of a 134.5 MB file    # 0.02 s

(<root> stands for the 64-hex reference swarmlite publish prints; the CLI will remind you if a placeholder slips through.)

Warm repeats fetch nothing; an FTS5 full-text search fetched 12 pages.

Status: v2. The Python read path (swarmlite.connect, swarmlite query), the publisher (swarmlite publish, with feed support), and the browser reader (js/, SQLite-WASM — no install, no wallet, no extension) are implemented, tested (offline suites + opt-in live round-trips), and demonstrated live, including bzzf:// feed reads and a self-contained demo site. docs/USER_GUIDE.md has the complete setup and worked examples; the lookup tables — every export, signature, flag and URL form, pinned against the code by the test suite — are in docs/REFERENCE.md (the document to hand to an AI agent); design in docs/DESIGN.md, plan in docs/roadmap.md.

How it works

SQLite never touches storage directly — all I/O goes through its VFS interface, and a read-only VFS needs three methods. SQLite's default page (4 KB) equals a Swarm chunk; a query walks a B-tree, so even on a multi-GB file a point lookup touches ~4 pages. swarmlite maps xRead(offset, n) onto swarmfs range reads, with an LRU page cache in front:

application          plain SELECT
SQLite engine        unmodified (apsw)
bzz-VFS shim         xRead(off, n) -> ranged fetch     <- this package
swarmfs              f.seek(off); f.read(n)
Bee                  /bytes, HTTP range reads

Writes stay where SQL writes belong: the publisher works on a local file — full SQL, real transactions — then ships an immutable snapshot and optionally advances a feed. Every publish is a permanent snapshot; the old roots keep working. (Precedent for the read path: phiresky's sql.js-httpvfs over plain HTTP ranges; Swarm adds verification and permanence.)

Quick start

pip install swarmlite            # PyPI: pulls swarmfs automatically

or for development:

git clone https://github.com/petfold/swarmlite
cd swarmlite
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[test]"

pytest                           # 43 tests, no node needed
python examples/offline_demo.py  # the demo, offline — no node, no funds

The offline demo runs the identical code path through an in-memory filesystem: a cold point lookup on a 134 MB database fetches 4 pages.

Going live

With a Bee node (e.g. Swarm Desktop) and a usable postage stamp — see the User Guide for stamp buying and sizing. The demo above ran in RAM, so first materialize a database file to publish (or use your own SQLite file):

python -c "import sys; sys.path.insert(0, 'examples');
from offline_demo import build
open('demo.db', 'wb').write(build(rows=30000))"

swarmlite publish demo.db
# pin:  bzz://<root>/demo.db      <- the 64-hex root hash is minted HERE

That printed root is the database's content address — save it; it is also re-derivable by publishing the identical file again. Then:

swarmlite query "bzz://<root>/demo.db" \
    "SELECT title FROM posts WHERE id = 12345" --stats

No stamp yet? swarmlite publish demo.db --buy prices a batch sized for the file, shows the xBZZ cost, and buys it from the node's wallet.

A published root lives exactly as long as its postage batch, and an expired batch cannot be revived — so renewal is part of publishing:

swarmlite stamps                          # life left, and bucket headroom
swarmlite stamps --check --min-ttl 7d      # exit 1 when one needs renewing (cron)
swarmlite stamps topup <batchID> --for 4w  # extend it (asks before spending)

Migrating a blog? examples/wordpress/ turns a standard WordPress export (Tools → Export) into a searchable blog under one Swarm root — list, search, and every post page are queries against one published file.

If the data will ever change, publish into a feed from the first version — the same single upload advances the signed feed AND yields the pin, and readers get a stable URL from day one:

swarmlite publish demo.db --feed mysite --signer <private key hex>
# pin:  bzz://<root>/demo.db
# feed: bzzf://<owner>/mysite/demo.db

Every publish is a permanent snapshot; swarmlite snapshots "bzzf://<owner>/mysite/demo.db" lists the whole version history, each line a pinned bzz:// URL you can still query.

import swarmlite

con = swarmlite.connect(f"bzz://{root}/demo.db")
rows = list(con.execute("SELECT ... "))
print(con.swarmlite_file.stats())   # pages/bytes fetched vs. file size

URL forms: bzz://<root>/site.db pins an immutable version; bzzf://<owner>/<topic> follows a feed to the latest. file:// and memory:// work too (tests, local use). Connections are strictly read-only — DML raises apsw.ReadOnlyError.

In JavaScript (browser and Node)

The same lazy-page trick runs inside SQLite-WASM. npm install swarmlite gets the reader and a pure-JS publisher, zero runtime dependencies: a static page, the reader, the wasm engine and the database all publish under one immutable Swarm root — a multi-GB dataset behind a static site, no server, nothing to install for readers. Full-stack JS developers never need the Python side:

npx swarmlite publish site.db --feed mysite --signer <key hex>
npx swarmlite query "bzz://<root>/site.db" "SELECT ..." --stats --verify

Feed signatures are byte-identical across the two implementations (asserted against shared fixtures), so JS and Python publishers are fully interchangeable.

python js/demo/publish_site.py --stamp <batchID>
# site: http://localhost:1633/bzz/<root>/index.html

Measured live: a cold point lookup fetched 5 pages (20 KB) of a 41.9 MB database; a whole 4-query session 23 pages (92 KB). Feeds resolve in the browser too (resolveFeed), so the page can always show the latest published snapshot. Against an untrusted gateway, verify: true checks every byte client-side against the 32-byte root (BMT per chunk, signature recovery per feed update) — a tampering gateway is caught on the first bad chunk. User Guide §7 has the details.

What this is not

  • Not a SQL engine over Swarm-native indexes — that is recordstore/POT territory.
  • Not OLTP. Feeds are eventually consistent; pin a root when you need reproducibility.
  • Not a write path through the VFS. The publisher pattern is the write API.

Relatives

  • swarmfs — the fsspec backend this builds on (range reads, transactional writes, feeds). Its API is the test-pinned REFERENCE.md.
  • recordstore — versioned key→record store; the natural system of record that a published site.db is materialized from. Likewise: REFERENCE.md.
  • DuckDB + Parquet over range requests — the analytics flavour of the same trick: cookbook with live measurements. And your Postgres/MySQL can feed all of this — the read-replica cookbook shows how.

License

BSD 3-Clause. Author: Peter Foldiak.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

swarmlite-0.3.1.tar.gz (578.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

swarmlite-0.3.1-py3-none-any.whl (23.3 kB view details)

Uploaded Python 3

File details

Details for the file swarmlite-0.3.1.tar.gz.

File metadata

  • Download URL: swarmlite-0.3.1.tar.gz
  • Upload date:
  • Size: 578.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for swarmlite-0.3.1.tar.gz
Algorithm Hash digest
SHA256 5e2b1c6e8be14dcdb35e6e61eb579f2ca2c0ce0cffd88cd88b78a269edb546ac
MD5 7861422c607653a7ad5d758b39e0b250
BLAKE2b-256 80fc79be996dbdf45324bde1372331025a86bb8132b0510dc6d234fc3f498d45

See more details on using hashes here.

Provenance

The following attestation bundles were made for swarmlite-0.3.1.tar.gz:

Publisher: publish.yml on petfold/swarmlite

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file swarmlite-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: swarmlite-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 23.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for swarmlite-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e06ea22560a981a4cb109d0276439262c734b31fa02bc78c3e55db390d33985e
MD5 817e791fbad6c50255bcde27c2306cfd
BLAKE2b-256 3a76e6bd51f1df19b3a441f0a8231f8567eaef36cbb3fec9eb73c3112a84c122

See more details on using hashes here.

Provenance

The following attestation bundles were made for swarmlite-0.3.1-py3-none-any.whl:

Publisher: publish.yml on petfold/swarmlite

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.3.1 This release

2 files

0.3.0

2 files

0.2.1

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page