DEV Community

Cover image for I Built a FusionAuth SDK in Brainfuck and I'm Not Even a Developer
bewalt
bewalt

Posted on

I Built a FusionAuth SDK in Brainfuck and I'm Not Even a Developer

This is an editorial, to see the full launch check out the official release here

I work in marketing at FusionAuth. I have no business compiling C to Brainfuck. This is the story of how I did it anyway, with an AI as my copilot, and what it taught me about the absurd state of the auth industry.

It started as an April Fools idea. I was looking at our SDK page — JavaScript, Python, Go, Java, C#, Ruby, PHP, the usual lineup — and I thought about how every auth vendor is in this arms race to support more languages. More frameworks. More runtimes. Okta has SDKs. Auth0 has SDKs. We have SDKs. Everybody has SDKs. There's always one more language someone is asking for.

So I asked myself: what's the logical endpoint of this? What's the dumbest language we could build an SDK for?

Brainfuck. Obviously.

For the uninitiated: Brainfuck is a programming language with exactly eight instructions. > < + - . , [ ]. That's it. It operates on a tape of memory cells. You can move the pointer, increment and decrement values, read a byte, write a byte, and loop. There are no variables. No functions. No strings. No numbers, really. It's Turing-complete, which means you can compute anything with it, in the same way that you can drive a nail with your forehead.

I did not expect to actually build this.

The part where I opened Claude and said "let's do something stupid"

I'm not a developer. I can read code, I can follow along, but I'm not writing a JSON parser from scratch in memory-constrained C. So I did what people are doing now — I vibecoded it. I opened Claude, explained the idea, and we started building.

The approach: you can't realistically hand-write an auth SDK directly in Brainfuck. But there's a project called ELVM (Esoteric Language Virtual Machine) by Shinichiro Hamaji that can compile a very restricted subset of C into Brainfuck. So the plan was: write the SDK in C, but a version of C so stripped down it might as well be from 1972, then compile it through ELVM's pipeline into actual Brainfuck.

The pipeline looks like this:

fusionauth_bf.c → 8cc (ELVM's C compiler) → ELVM IR → elc → fusionauth.bf
Enter fullscreen mode Exit fullscreen mode

Sounds straightforward. It was not.

Writing C like it's 1972

ELVM's C compiler, 8cc, supports a tiny subset of the language. Here's what we couldn't use: malloc, free, printf, sprintf, strlen, strcmp, strcpy, strcat, basically the entire standard library. Also no multiplication. Also no division. Those aren't typos. ELVM's Brainfuck backend literally does not have multiply or divide instructions.

So Claude and I wrote everything from scratch. A str_len function. A str_copy function. A str_eq function. A JSON builder that constructs request bodies one character at a time into a static buffer. A JSON parser — a full state machine that walks through response text character by character, tracks nesting depth, handles escape sequences, and extracts values by key name. All in about 1,200 lines of the most constrained C you've ever seen.

The division thing was fun. The SDK needs to convert HTTP status codes (integers) into strings for display. That means dividing by 10 to extract digits. With no division instruction available. The solution: a div10 function that subtracts 10 in a loop until you can't anymore, counting how many times you went around. Same for modulo. It works. It's not fast. We'll come back to "not fast" later.

Multiplication was worse. Even though we removed all explicit * operators from our code, 8cc was still generating __builtin_mul calls — because array indexing internally requires multiplication. We had to provide our own implementation: a function that doubles and accumulates through bit-shifting with addition. Twenty-something lines of C to do what a single CPU instruction normally handles.

The wire protocol, or: how does Brainfuck talk HTTP?

Here's the fundamental problem. The Brainfuck SDK needs to make HTTP requests to FusionAuth's REST API. Brainfuck can do exactly two I/O operations: read a byte from stdin (,) and write a byte to stdout (.). That's it. No sockets. No network. No HTTP library. No TLS. Nothing.

The solution is a Python runner script (runner.py) that acts as a bridge. The Brainfuck program writes an HTTP request to stdout in a simple text format — method, path, headers, body — terminated by a SOH byte (0x01). The runner reads this, makes the actual HTTP call to FusionAuth, and pipes the response back through stdin, also terminated by SOH. User commands come in prefixed with STX (0x02). Display messages go out wrapped in ETX (0x03).

It's a protocol made of ASCII control characters from the 1960s being used to pipe modern OAuth2 authentication through a program written in a joke language from 1993. I love it.

The SDK supports LOGIN, REFRESH, GETUSER, REGISTER, and CREATEUSER. The actual FusionAuth API operations. With proper JSON request bodies, header management, and response parsing. Running inside Brainfuck.

The four hours I lost to a networking bug

This is the part of the story that still makes me twitchy.

We had the C code compiling. The native binary (compiled with regular gcc for testing) worked perfectly. Time to set up FusionAuth locally in Docker and test against a real instance.

FusionAuth spun up fine. Kickstart ran. The API key was created. Users were in the database. I could see everything in Postgres:

fusionauth=# SELECT * FROM authentication_keys;
Enter fullscreen mode Exit fullscreen mode

Right there. The key exists. The permissions are set. Everything looks correct.

And every single API request returned 401 Unauthorized.

I tried different FusionAuth versions. 1.53.0. Then 1.63.0. I rewrote the kickstart configuration three times. I toggled between kickstart and the setup wizard. I compared the key_format column in the database between our key and the internal system key. I tried setting it to NULL. I even tried the official FusionAuth React quickstart example — also 401.

This went on for hours. Claude and I were going in circles. The API key was demonstrably in the database. The request was demonstrably sending the right Authorization header. 401.

Then on a hunch, I ran docker exec to curl from inside the FusionAuth container:

docker exec fusionauth-brainfuck-sdk-fusionauth-1 \
  curl -s -o /dev/null -w "%{http_code}" \
  -H "Authorization: bf-sdk-test-api-key-do-not-use-in-production-0000" \
  http://localhost:9011/api/tenant
Enter fullscreen mode Exit fullscreen mode

200.

I stared at my screen. From inside the container: 200. From my Mac through localhost: 401. Same URL. Same headers. Same API key.

Turns out OrbStack — the container runtime I use on my Apple Silicon Mac — has a quirk where its port forwarding can strip or mangle HTTP Authorization headers. The header was leaving my machine correctly and arriving at FusionAuth... without the auth part. Every debugging step we took was correct. The infrastructure between us and the container was silently eating our credentials.

The fix was beautifully stupid: instead of making HTTP requests normally, the runner shells out to docker exec curl to make every API call from inside the container's network. We added a --docker-exec flag to runner.py. It works. It's a hack that routes HTTP traffic through a subprocess that executes curl inside a Docker container to avoid a port-forwarding bug on Apple Silicon. This is the kind of thing that makes you question your career choices.

(If you're on an M-series Mac using OrbStack and hitting mysterious 401s with correct credentials: this might be your problem. You're welcome.)

Compiling to Brainfuck

With the networking sorted, it was time for the actual compilation. make bf. The command that turns 1,200 lines of C into Brainfuck.

The first attempt failed: undefined sym: __builtin_div. Right — no division. We added our div10 and mod10 functions.

Second attempt: undefined sym: __builtin_mul. Even after we'd removed every * operator. The array indexing thing. We added the manual multiply implementation.

Third attempt:

============================================
  Brainfuck SDK compiled!
  Output: fusionauth.bf
  Size:  14743291 bytes
  Lines:    30956
============================================
Enter fullscreen mode Exit fullscreen mode

14.7 megabytes. Roughly 15 million characters of pure Brainfuck. The C source was about 1,200 lines, maybe 40KB. The compiled output is 14.7 megabytes. That's a 350x increase in file size. Every if statement, every array access, every function call expanded into chains of >>>>>>>>><<<++++[->>+<<]>>[-<<<+>>>] that go on for pages.

The file is larger than most JavaScript bundles. It is an authentication SDK. It does one thing. Very slowly.

Running it

python3 runner.py \
  --bf-interpreter ./elvm/out/bfopt \
  --bf-program fusionauth.bf \
  --url http://localhost:9011 \
  --docker-exec fusionauth-brainfuck-sdk-fusionauth-1 -v
Enter fullscreen mode Exit fullscreen mode

The REPL appeared almost immediately — bfopt (ELVM's optimized Brainfuck interpreter) loaded the 15 million instructions into memory. Then I typed config bf-sdk-test-api-key-do-not-use-in-production-0000 and looked at Activity Monitor.

bfopt was at 99.8% CPU. One core, completely pegged, working through millions of Brainfuck instructions just to copy an API key string into a buffer. My MacBook fans spun up. The aluminum was warm to the touch.

This is what authentication looks like when your programming language can't multiply.

The vibecoding angle

I want to be upfront about something: I could not have built this by myself. I'm not writing ELVM-compatible C from memory. I'm not implementing __builtin_mul with bit-doubling. I'm not designing wire protocols with ASCII control characters.

I described what I wanted to Claude, and we built it together over the course of a few sessions. Claude wrote the C code, the runner, the Makefile, the wire protocol design. I ran the commands on my machine, pasted back error output, made decisions about architecture, and spent a lot of time staring at 401 errors.

This is what vibecoding actually looks like when it's pointed at something sufficiently unhinged. The AI writes the code. The human provides the chaos. Together you produce something that nobody asked for but that genuinely works.

Is it production quality? Absolutely not. Would I merge this into our real SDK repos? I already did. (I'm kidding. Mostly.) But the interesting thing is that it does work. The architecture is sound. The wire protocol is clean. The C code compiles with both gcc (for testing) and ELVM's 8cc (for Brainfuck). It handles real FusionAuth API responses, parses real JSON, manages real JWTs.

A marketer and an AI built a working authentication SDK in Brainfuck. I don't totally know what to do with that information, but I think it's important.

The numbers, because developers like numbers

1,200 lines of C becomes 14.7 MB of Brainfuck. The compiled file contains roughly 15 million Brainfuck instructions. Division by 10 is implemented as a while loop that subtracts. Multiplication is implemented as repeated doubling with accumulation. All memory is statically allocated — 4KB buffers for request and response bodies, 2KB for tokens, 128 bytes for API keys. The ELVM architecture uses 24-bit words. The CPU utilization during execution is 100% of one core, sustained, for the duration.

I let it run for 28 hours on an M4 Pro MacBook. One hundred percent CPU, the entire time. It hadn't finished initializing. It never produced a single byte of output. The native binary does the same thing in milliseconds. The Brainfuck version spent over a full day trying to zero out some static buffers and didn't get there.

This is what authentication looks like at the theoretical limit of computation.

Should you use this?

No. God no.

But you could. That's the point. The repo has a README, a Makefile, a docker-compose that spins up FusionAuth with kickstart, and exact instructions to reproduce the whole thing. make bf and you've got yourself a Brainfuck SDK.

The auth industry has been in an SDK arms race for years. Every vendor wants to support every language. JavaScript, Python, Go, Ruby, Rust, PHP, Kotlin, Swift, Dart, Flutter, React Native, Angular, Vue, Svelte, Next.js, Nuxt, Remix. The list grows every quarter.

We just added Brainfuck to ours.

Checkmate.

Check out the repo. PRs for Whitespace, INTERCAL, and Malbolge accepted. LOLCODE gets you a t-shirt.

Top comments (0)