Queryable Executables

Aug 26, 2026 07:20 AM - 2 hours ago 1

I was pleasantly amazed and happy to spot that my article ‘Your executable is simply a SQLite database’ resonated pinch people. It is simply a format I person been reasoning astir for a while, and the thought seems to have struck a chord pinch others.

meme of Danny from Ted Lasso saying sqlite is life

A speedy recap: SELF, a format wherever the programme is simply a SQLite database. We tin usage binfmt_misc to trigger a custom interpreter that maps the rows successful the segments array and jumps to the introduction point, and a full people of binary tooling collapses into SQL.

What keeps astonishing maine is really having the record format beryllium a SQLite database keeps collapsing everything into SQL. One thought that was instantly evident to myself and others done comments: If the executable is simply a database, and a database is thing you tin constitute to, tin the running program usage it to besides shop its state? 🤔

Yes! 🤯 We tin illness not only a complete distribution but each the authorities for each exertion into a azygous file, alleviating the request for /var/ aliases /tmp/ aliases /home/ aliases immoderate different filesystem. The programme tin shop its ain authorities successful the aforesaid record it is moving from, and it tin do truthful transactionally.

self-httpd is simply a proof-of-concept webserver that does precisely that. It is simply a azygous record programme executed from a database. The record contains the program, the website, the routes and each the visitant logs. All authorities is updated in the aforesaid SQLite record arsenic the programme itself.

# Our server is simply a azygous file, and it is simply a SQLite database $ file server server: SQLite 3.x database, exertion id 1397050438, ... $ ./server --journal wal 8080 self-httpd: serving 3 routes retired of /srv/self/server self-httpd: listening connected http://0.0.0.0:8080 pinch 4 workers $ curl -s localhost:8080 | head -1 <!doctype html> # cipher has pressed the fastener connected that page yet $ sqlite3 server 'SELECT count(*) FROM presses' 0 $ curl -s -X POST -d property localhost:8080/api/press {"presses":1,"button":"press"} # the exertion information is wrong the aforesaid database $ sqlite3 server 'SELECT id, at, fastener FROM presses' 1|2026-08-25 03:11:28|press # truthful was the GET that fetched the page successful the first place $ sqlite3 server 'SELECT count(*) AS n, path FROM visits GROUP BY path' 1|/ 1|/api/press

This web-server is unrecorded astatine https://selfdb.exe.xyz.11If the tract is not moving for you, sorry. I deployed it connected their smallest tier. I included a screenshot of the tract conscionable successful lawsuit for posterity!  It is 1 file, a SQLite database, and it is besides the server. It is the website, it is the program, and it is the visitant log and state.

 13 segments, 179 symbols, 105 relocations, 2 needed libraries, 3
routes, 12 tables, 103 visits recorded, 24 presses
recorded.

I person a batch of admiration for the activity of Justine Tunney, whose anterior creation redbean: a webserver successful a single file, built arsenic an Actually Portable Executable pinch a self-extracting ZIP archive, inspired the idea.

SELF is galore ways is little brilliant. It relies connected simpler devices to execute something very akin but I’m amazed really overmuch collapses into a azygous domain: SQL.

Whereas, redbean needs to see an archive format (ZIP), the database itself is the container. Redbean provides Lua hooks to manipulate the responses, whereas the balanced successful SELF is simply a caller statement successful a handlers table.

INSERT INTO handlers VALUES ('/api/busiest', 'SELECT path, count(*) FROM visits GROUP BY path ORDER BY 2 DESC LIMIT 5');

If redbean is an Actually Portable Executable, this is an Actually Queryable Executable. One of them runs anywhere, the different 1 you tin SELECT from.

How does the process get entree to itself? 🤔

For now, you cannot usage /proc/self/exe.22Funny enough, the VFS Linux maintainer precocious landed support for transparent binfmt_misc successful the kernel, which would make /proc/self/exe constituent to the original file. I wrote about it here.  When binfmt_misc matches, the kernel does not execve your record astatine each , it execs the interpreter, and hands it the path:

self-exec passes argv + 1 done to the program, truthful the program’s argv[0] is the way to the executable itself. The expert besides releases its SQLite relationship earlier jumping to the introduction point, truthful the programme tin unfastened its ain record and query it.

int main(int argc, char **argv) { sqlite3 *db; /* the record the kernel conscionable executed */ sqlite3_open(argv[0], &db); ... }

This is beautiful unrestricted and magical. You tin publication your ain conception array aliases a caller array adjacent to it. The writes persist crossed invocations. ✨

The web-server for our illustration is 3 tables: routes, visits and presses. We will grounds each visitant and each fastener press.

-- the content, added to the executable -- aft it is compiled and linked CREATE TABLE routes (path TEXT PRIMARY KEY, mime TEXT, body BLOB); -- what the tract collects, written backmost -- into the executable while it runs CREATE TABLE visits (id INTEGER PRIMARY KEY, at TEXT, ua TEXT, path TEXT); CREATE TABLE presses (id INTEGER PRIMARY KEY, at TEXT, button TEXT);

Building the exertion feels very unremarkable and familiar. We execute DDL to create the exertion schema and INSERT the website.

# an mean ELF for now $ cc -O2 server.c -o server.elf $(pkg-config --libs sqlite3) # the aforesaid program, arsenic rows $ elf2self server.elf server $ sqlite3 server < site/schema.sql $ sqlite3 server "INSERT INTO routes VALUES ('/index.html', 'text/html', readfile('site/index.html'))"

The plus pipeline looks for illustration a “normal webserver” until you recognize it’s querying itself with SQL for the content. Oh, and “itself” is simply a SQLite database.

cluster_file server — the aforesaid file! req GET / proc running server req->proc krn execve() binfmt_misc se self-exec krn->se se->proc map, jump seg segments (the program) se->seg SELECT content rsp 200 OK proc->rsp rt routes (the website) proc->rt SELECT body vis visits (the log) proc->vis INSERT

The page astatine https://selfdb.exe.xyz shows a batch of nosy further accusation besides the visitant log and fastener presses. I included segments, symbols and relocations. Those are not baked successful astatine built time, they are queried from itself while running.

Once you person the capacity to do ACID transactions, absorbing things go possible. The webserver tin edit its ain contented while it is running, and the edits are transactional. The UPDATE is committed to the aforesaid record arsenic the program, and a ROLLBACK undoes it.

# alteration the moving site. nary restart, nary reload, nary deploy $ sqlite3 server "UPDATE routes SET assemblage = readfile('new.html') WHERE way = '/index.html'" $ curl -s localhost:8080 <!doctype html><h1>edited successful place</h1>

Since the record format is SQLite we tin besides return advantage of the cornicopea of tooling that exists. sqldiff will show you precisely what a “deploy did”, this tin fto america audit and place changes betwixt 2 versions of the aforesaid program.

$ sqldiff --summary yesterday.server server routes: 1 changes, 0 inserts, 0 deletes, 2 unchanged segments: 0 changes, 0 inserts, 0 deletes, 13 unchanged symbols: 0 changes, 0 inserts, 0 deletes, 174 unchanged relocations: 0 changes, 0 inserts, 0 deletes, 99 unchanged

What astir full-text search? FTS5 is simply a CREATE VIRTUAL TABLE away, truthful a webserver tin scale its ain pages, wrong itself, and still beryllium a webserver afterwards:

$ sqlite3 server "CREATE VIRTUAL TABLE hunt USING fts5(path, body); INSERT INTO hunt SELECT path, assemblage FROM routes WHERE mime LIKE 'text/%'" $ sqlite3 server "SELECT path, snippet(search, 1, '[', ']', '...', 6) FROM hunt WHERE hunt MATCH 'transaction'" /index.html|...Editing is simply a [transaction].</h2> # still runs. it conscionable knows astir itself now $ ./server 8080

None of that is machinery I wrote. It is machinery SQLite already has, that a program inherits for free by being a database.

All the rage was fixed tract generators, but the early is an actually queryable executable.

I americium really enjoying the simplicity that seems to beryllium celebrated and heralded by products for illustration exe.dev. People often yearn to spell backmost to the “good aged days” of scp and ssh to deploy a azygous file, and SELF is simply a format that makes that imaginable again, but better! Rather than conscionable shipping an archive of PHP, we vessel the full strategy aliases exertion closure down to the libc.

How would we make a deployment if the information and codification is intertwined?

We tin deliberation of a redeploy arsenic a information migration, and the migration is 2 INSERT ... SELECT, because the programme and its information are the same file!

-- the moving deployment ATTACH '/srv/self/server' AS old; INSERT INTO visits (at, ua, path) SELECT at, ua, path FROM old.visits; INSERT INTO presses (at, button) SELECT at, button FROM old.presses;

Swap the file, restart, and the visitant log survives the caller build. You tin moreover do this for the programme itself successful reverse. The segments array is conscionable for illustration immoderate different table. 😈

https://selfdb.exe.xyz has a fastener connected it. Pressing it is an INSERT into the executable that served you the page

The codification is astatine fzakaria/selfdb if you are curious. It is astir apt a spot half-baked, and decidedly AI assisted, but that’s OK pinch me. I wanted to research this thought and spot if it was feasible and what mightiness beryllium possible.

I deliberation I only scratched the aboveground of immoderate of the nosy possibilities. I americium funny to spot what others mightiness do pinch it, and I would emotion to spot a fewer much examples of “actually queryable executables” successful the wild.33One thought a friend suggested was find complete multicase DNS to spread programme updates via transactions. 

Turns retired that erstwhile we re-envision what we considered to beryllium simply a byte layout specification was really amended disconnected being a database, a batch of machinery we person been utilizing for decades simply stops being necessary. The programme is the database, and the database is the program.

“Never, ever underestimate the value of having fun”

– Randy Pausch

More