==Lowband Inc.==

Volume Two, Issue Seventeen, Phile 04 of 11

slab: a paste server in 900 lines

by mox

30 June 2026

0x00 Introduction

I wanted somewhere to put a config file so a friend could read it five minutes later. Every hosted pastebin I tried wanted an account, ran three trackers, or quietly kept the text forever. The self-hosted ones wanted a database and a PHP runtime.

slab is what I wrote instead. One binary, one file on disk, no database. It has been running on a virtual machine with 256 MB of memory for eleven months and the process has never gone above 9 MB resident.

[contents]

0x01 What it does

You POST some text and get back a URL. Anyone with the URL can read the text until it expires. That is the whole product.

Syntax highlighting is done at write time, not read time, so the read path is a file offset and a copy. That decision is the reason a paste server needs 900 lines rather than 9000.

[contents]

0x02 Building it

    $ git clone https://example.org/slab.git
    $ cd slab
    $ make
    cc -std=c11 -O2 -Wall -Wextra -o slab slab.c
    $ ./slab -d ./data -l 127.0.0.1:7070

The only dependency is libc. It builds with gcc and with clang, and it builds on OpenBSD, which is where it runs. There is no configure step because there is nothing to configure at build time.

Run it behind whatever terminates TLS for you. It speaks plain HTTP/1.1 and it will not pretend otherwise.

[contents]

0x03 The on-disk format

Everything lives in one append-only file called slab.dat. A record is a fixed header followed by the bytes:

    offset  size  field
    ------  ----  -----------------------------------------------
         0     4  magic, "SLAB"
         4     1  version, currently 1
         5     8  created, unix seconds, big endian
        13     8  expires, unix seconds, big endian
        21     4  length of the body in bytes
        25     4  crc32 of the body
        29     n  body

The lookup key is the offset itself, base32 encoded with a four character check tail. That means the URL is short, the read is one pread, and there is no index to corrupt.

It also means the file only grows. A nightly job walks the records, copies the ones that have not expired into a new file, and swaps it in with rename. On the box I use, eleven months of pastes compacted from 2.1 GB down to 340 MB in about four seconds.

[contents]

0x04 Talking to it

    $ curl --data-binary @sshd_config https://paste.example.org/
    https://paste.example.org/mq4h7t2xk9

    $ curl -H 'X-Slab-Expire: 3600' --data-binary @note.txt \
        https://paste.example.org/
    https://paste.example.org/8vv2p0c1nd

    $ curl https://paste.example.org/mq4h7t2xk9/raw

Append /raw for the bytes as they went in, with no HTML around them. Anything else renders the highlighted version that was built at write time.

The server answers a HEAD with the expiry in a header, which is enough for a shell script to decide whether to re-upload.

[contents]

0x05 Known bugs

Compaction blocks writes
The swap takes a write lock for the whole copy. On a big file that is a few seconds where POSTs get a 503. Nobody has complained yet, which probably says more about the traffic than the code.
The highlighter guesses badly on plain prose
It looks at the first 200 bytes and votes. An English paragraph that happens to start with a hash character comes out as a shell script.
No IPv6 literal in the listen argument
The parser splits on the last colon, so -l [::1]:7070 does not work. Patch welcome.

[contents]

0x06 Dispatches

Notes from the author, pulled from Nostr when you open the page.

[contents]

0x07 Greets

To rk for the crc32 table and for telling me the first version of the header was two bytes too clever. To the people who ran the Sunday build box for six years without being asked. To whoever left a copy of the 1994 issues on a gopher hole in Trondheim.

Mail to mox at lowband dot org. Plain text, please.

[contents]

EOF

read-only

add #edit to the URL to edit this page