← Back to projects

COBOL is the language people reach for as a punchline. It also runs a large share of the world's financial transactions and is not going anywhere. I wanted to find out whether you could build a genuinely correct HTTP/JSON API in it and put it on the public internet as a real service: one that behaves properly, handles its error cases, and stays up.

A live, public JSON API served entirely by GnuCOBOL, with correct HTTP semantics and a real deployment. It answers health checks, returns structured JSON, handles its error cases with the right status codes, and sits behind the same reverse proxy and TLS as everything else I run. You can curl it right now and get a well-formed response.

01

socat owns the socket, COBOL owns HTTP

A single line of socat holds the listening socket and forks one short-lived COBOL process per request, handing it the connection over pipes. Everything after that is COBOL: reading the raw request bytes, parsing the request line and headers, routing on method and path, and writing the status line, headers, and JSON body. There is no web framework and no runtime underneath doing the HTTP for it.

02

Routing and JSON errors by hand

The router is a set of COBOL paragraphs that match method and path and dispatch to a handler. Every handler emits well-formed JSON, including the error paths: 400 for a malformed request, 404 for an unknown route, 405 for the wrong method, 413 for an oversized body. Getting the error semantics right is most of what separates a real API from a toy that only handles the happy path.

03

The column-72 constraint

Compiled in fixed format, the source has a hard right margin at column 72. The landing page's HTML, CSS, and JavaScript are hand-fragmented across roughly a hundred short string literals and spliced with STRING ... WITH POINTER. Newlines are placed by hand as X"0A" because GnuCOBOL does not process backslash escapes, and the emitted JavaScript builds its own newlines with String.fromCharCode(10). The constraint makes you deliberate about every byte you emit.

04

Boring operations on purpose

It deploys like any other service on the box: a rootless Podman container behind the same Traefik reverse proxy as everything else, with a wildcard TLS certificate and a systemd user unit to keep it alive. The language is unusual. The operational story is deliberately ordinary.

// the language is incidental

The language is incidental

A production web service is a set of disciplines rather than a language: correct status codes, structured errors, a clean request lifecycle, and an ops story. COBOL can satisfy all of them. Language fashion is not the thing that makes a service real.

// constraints force clarity

Constraints force clarity

A column-72 margin and no string escapes sound like obstacles. They make you think about exactly what leaves the socket, which is a good habit in any language that gives you more rope.

// ops transfers everywhere

Ops transfers everywhere

The rootless-container-behind-Traefik pattern here is the same one every other service on the box uses. Once the deployment shape is repeatable, adding a new service is cheap regardless of what it is written in.

GnuCOBOL 3.2socatPodmanTraefiksystemd
// more

See the rest of the work.