blindrange

Build on blindrange — web apps, no backend

One static file is the whole application. Keys are derived in the page, records sealed in the page, queries planned in the page; the network supplies durability, sync between devices, and uniqueness arbitration — while being unable to read a byte of it. Host the file anywhere that serves HTML, or nowhere in particular.

1Start from a complete app

Splitlist is a working multi-device shared checklist in ~130 lines, most of them comments — read the file, save it, open it. It shows the whole model: open-or-create, one indexed field, sealed payload fields, network-arbitrated ordering, invites as the only auth. Copy it and change the schema.

# the entire deployment procedure:
cp starter.html anywhere-a-browser-can-reach/

2The API on one screen

import { Owner, idbAdapter } from "https://blindrange.dev/blindrange.js?v=3";

const db = await Owner.create(idbAdapter("myapp"), passphrase, SCHEMA,
    ["seed.blindrange.dev:7501"],
    { networkSecret: "blindrange-public",
      gateway: ["https://blindrange.dev/api"] });   // list = failover

await db.insert({ amount: 42, note: "sealed fields travel free" });
await db.query("amount", 0, 100);        // range → [{...rec, _rid}]
await db.queryPrefix("name", "sa");      // str fields: prefix search
await db.delete(rid);                    // tombstone by _rid
await db.nextValue("invoice");           // unique across ALL writers
await db.nextValues("invoice", 10);      // block, one round trip
db.invite();                             // onboarding string = master key
await Owner.accept(adapter, pass, invite);  // join from an invite
await Owner.open(adapter, pass);         // reopen this device's state
await db.enableMirror("myapp-mirror");   // local-first reads, persistent
await db.drain();                        // await hedged background copies

Schema fields are what you can range-query; every other key in a record is carried sealed and costs nothing. BLUR (leaf_width) is the resolution the network may ever distinguish — the SQL guide explains the privacy budget; the same rules apply here.

3Auth is key distribution

There are no accounts. Whoever holds the state (this device, unlocked by the passphrase) or an invite (any device, any language — Python's Owner.accept takes the same string) is a writer. This is the honest scope of v1: apps for a person or an invited group — which is most small software. Scoped capabilities (read-only, one table) are a design item, not a feature; nothing on this page pretends otherwise.

4Security you own — not optional

Any script in your page can read the master key. The rules are short and absolute: vendor blindrange.js next to your page (best) or pin the import with an integrity hash; send a strict Content-Security-Policy; and never add a third-party script, analytics tag or CDN widget to a page that holds keys. XSS here is not a cookie theft — it is the database.
The passphrase is the recovery story. PBKDF2-sealed state in this browser, invites as the only other copy of the master. Lose both and the data is unrecoverable by design — the same property that locks the storage provider out, pointed at your user. Say so in your UI.

5What to expect, measured

operationpublic demo network, via gateway
create / first open~3.5–4.5s
insert (quorum-acked)~0.6–1.5s — returns at 2 acks, third copy lands in the background (drain() awaits it)
query, first, no mirror~8s. Not the data — round trips. Every phase of a query waits for the one before it, and a gateway hop costs 0.2–1.3s each way
query, repeat, no mirror~2s (cached chain counters)
sync(), complete local mirror~8s for a small ledger, ~20s for a few hundred records
query, mirrored2–9ms — local, and it stays local while you keep the mirror fresh

Read that table as one instruction: call sync() and answer from the mirror. The network path is honest and slow; the mirrored path is three milliseconds. Everything between those two rows is the reason the mirror exists.

The gateway adds one hop: browsers refuse plain-HTTP from HTTPS pages, so requests travel through a fronted node's /fwd. The gateway sees which nodes you talk to — nodes always could; it reads nothing — nodes never could. Pass several gateways; any direct node behind any TLS proxy is one.

6Honesty section

The demo network has a published secret and no durability promise — run your own nodes (and gateway: any node + Caddy) for anything real. Not in the browser client, deliberately: SQL dialect, compaction (run it from any Python writer of the same database), QUIC direct paths. The client is one dependency-free file whose every key derivation is pinned byte-identical to the reference by a cross-language test suite — if they ever disagree, the build fails, not your data.

blindrange.dev · try it live · the SQL guide · the Python guide · the Node.js guide · built on it · source