Databases with no server
This site has no server. The Portugal section is 19 JSON files in a git repository, 1.3 MB in all, each with its hash in a manifest. Two real database engines run over exactly those files in your browser, compiled to WebAssembly: SQLite, and Oxigraph speaking SPARQL 1.1. Nothing is uploaded, nothing is queried remotely, and when the tab closes the database is gone. The files are the database. The engines are readers.
Two consoles over one set of files
The SQL console
15 tables built from the JSON on load, 16 worked queries from a count-by-class to a recursive graph walk, and a box to write your own. Every example ran at build; its row count is printed beside it so your run can disagree.
Open the SQL console → SPARQL 1.1 · OxigraphThe graph console
4,001 triples — the same graph, the ontology inside the store, inverses declared with
owl:inverseOf and walked with a property path, labels in English and Portuguese so a path
reads aloud. 13 worked queries, each shown beside the same question in Cypher.
The file system is the database
Every fact on the Portugal section lives in one JSON file that owns it: the people in
people.json, the frozen pages in sources.json, the sessions in
sessions.json. The build derives the other shapes a reader might want — the graph in
graph.json, the same graph as RDF in triples.nt, the manifest with every hash
— and nothing is derived twice. There is no import step, no schema migration and no connection
string: the SQL console reads a loader spec, tables.json, that says which
file and which field each column comes from, so a reader who doubts a cell can open the file it came from.
Three things follow, and they are the argument for doing it this way rather than a cheaper way of doing the usual thing:
- A query is reproducible against a commit. The files are versioned by git; a result carries the site version it ran against, and this page's examples record the row count they returned at build. A database that answers differently tomorrow with no commit in between is a database that has been edited.
- A check is a query. The Portugal build runs fifteen gates in Python — no verb its own
inverse, every node naming a registered source, nobody in the graph who is not on the published list.
Each is expressible as a query here, and the SPARQL console runs one of them as an
ASK. When the rule and the check are the same sentence, the rule cannot drift from what is enforced. - The reader pays nothing and trusts nothing. No account, no key, no request to a server that
could log the question. The engine is 4.6 MB of vendored code, fetched once
and cached; the data is the same files the page is built from. An agent gets the same: both consoles
publish
window.__toolsafter atool:readyevent, the convention graphs.sgit.ai's universe reader set.
What this is not
It is not a graph database, and graphs.sgit.ai's position is inherited on purpose: “not a graph database pitch — the claim is that one grammar is the interface at every boundary, not that things are stored in a graph.” The JSON stays the source of truth. The SPARQL store is built from it on load and thrown away on close; nothing is ever written back. Its shipped page lists “browser SPARQL/Cypher, RDF in code” under what that site does not have; this page is where the estate now has it, as a reader over files rather than a store of record.
It is not a server in disguise. The whole dataset is downloaded, which is fine at 1.3 MB and would not be at a gigabyte; that is the honest scaling limit of the pattern, and the sgit.ai guidance already names the answer — manifests at build time, files on click. And it is not a write path: corrections go through the files, the build and a version, which is the point.
What is vendored, and its cost
| Engine | Build | Language | Bytes | Licence | |
|---|---|---|---|---|---|
| SQLite | sql.js 1.14.2 | SQL | 704,945 | MIT | console |
| Oxigraph | oxigraph 0.5.11 (web build) | SPARQL 1.1 | 4,099,865 | MIT OR Apache-2.0 | console |
Both are vendored under assets/vendor/ and attributed in
LICENSES.md, for the
reason the rest of the site's third-party code is: an evidence chain should not end in a resource that can
move. Cypher is shown beside each SPARQL query and not executed. Kùzu compiles to WebAssembly and
would run it here; its package is 73 MB unpacked, which is the wrong price for a demonstration, and
the choice is recorded rather than hidden.
The files each console loads
| File | Bytes | SHA-256 |
|---|---|---|
| changes.json | 2,149 | ad1d1e37da05 |
| checks.json | 1,457 | 61c2254b2570 |
| connections.json | 35,379 | 63af95dc9ded |
| coverage-notes.json | 3,481 | 0cef28eec4ad |
| coverage.json | 6,433 | 32a72cfba398 |
| event.json | 3,745 | 1217c9814409 |
| graph.json | 388,075 | c2d9281f4bd7 |
| lexicon.json | 13,193 | ba1cf8d3a588 |
| manifest.json | 23,359 | 599f470198d9 |
| notice.json | 8,189 | a9b919e0376a |
| ontology.json | 26,511 | 432d4fb588dc |
| orgs.json | 10,071 | ef3346010765 |
| people.json | 18,362 | bbc579b85be3 |
| sessions.json | 3,608 | dff95f982bf4 |
| sources.json | 43,920 | a5b0ab6d572a |
| stories.json | 2,168 | bf3f77a71f6e |
| team.json | 5,060 | f3e23c3fe955 |
| topics.json | 181,065 | 9c38f3505e8d |
| triples.nt | 622,006 | 96673dafd45c |
From manifest.json, which the file explorer renders and the Portugal gate re-verifies on every build.
For an agent
Two in-browser engines over /portugal/data/. SQL: open /databases/sql.html, wait for the tool:ready event, then window.__tools.sql.run(sql) returns {columns, rows, ms}; the loader spec is /databases/data/tables.json and the worked queries with their build-time row counts are /databases/data/queries-sql.json. SPARQL: /databases/graph.html, window.__tools.sparql.run(query); the triples are /portugal/data/triples.nt (4,001, N-Triples, IRIs under https://newsroom.sgit.ai/portugal/{id,verb,type,prop}/), the worked queries with Cypher equivalents are /databases/data/queries-sparql.json. Both pages fetch only same-origin files and send nothing anywhere.