This is a submission for the DEV Weekend Challenge: Community
In the comments of my last article, Sylwia wrote:
Awesomeπ
Now Iβm just waiting, Pascal, until you invent your own protocol β maybe something like a beer brewing protocol next? πΊ
I replied: "...HTBMCP/1.0 might be next. Hyper Text Beer Mug Control Protocol. Watch this space."
This is that space.
The Community
Before the code: a question worth asking.
The DEV prompt says "build for your community." The obvious answer, for a developer, is to build something useful for other developers β a tool, a library, a dashboard. Something that solves a real problem.
But there's a community that rarely gets named as such, and it's one I'm genuinely part of: the community of developers who find meaning in craft for its own sake. Who implement a coffee pot RFC not because it ships a feature, but because it teaches something real. Who write April Fools' specs with the same rigor they'd apply to production systems. Who understand that absurdity taken seriously is one of the most honest forms of technical education.
RFC 2324 has been cited, forked, debated, and defended for 26 years because it speaks directly to that community. It says: the standards process can be playful β rigorously, precisely playful. That's worth protecting.
HTBMCP/1.0 is a contribution to that tradition. It's for the developer who reads a comment about beer protocols at midnight and thinks yes, someone should actually do that. It's for @sylwia-lask, who asked. It's for anyone who's ever cited RFC 2324 in a code review and meant it.
What I Built
RFC 1516 β the Hyper Text Beer Mug Control Protocol (HTBMCP/1.0), to be published April 1st 2026. And its full implementation.
HTBMCP is the spiritual successor to RFC 2324 (HTCPCP, the coffee pot protocol). Where HTCPCP controls coffee pots, HTBMCP controls networked beer taps. It extends HTTP with five custom methods, three new headers, and one new error code.
The default port is 1414 β a memorial. In 1414, the municipal archives of Gdansk recorded the earliest known written attestation of the word "piwo" (beer, in Polish). That document was lost during the Second World War.
Port 1414 is therefore a memorial as much as a transport binding.
The implementation consists of four parts:
An interactive browser simulator β a self-contained HTML/JS file, no backend, no install. A complete HTBMCP state machine running in the browser with animated tap visuals, all six response codes, the full header set. Build the simulator before the server: it forces you to model the protocol as data before you model it as routes.
A raw asyncio TCP server (server.py) β because uvicorn rejects TAP, POUR, and WHEN at the socket layer. These are valid RFC 7230 tokens, but they're not in the IANA method registry. The fix is a minimal HTTP/1.1 parser over raw TCP that accepts any valid token as a method name. This is the correct approach: HTBMCP is its own protocol, and owning the transport layer is the honest implementation.
A FastAPI application (main.py) β used exclusively by the test suite. TestClient bypasses the HTTP transport entirely, so custom methods work fine there. You get structured routing and validation without the socket-level rejection.
41 tests covering the full protocol: every method, every error code, the Stout temperature MUST NOT, the goblet Trappist-only rule, brew_version conflict detection, the piwo:// Gdansk memorial tap, and the WHEN-once-is-sufficient invariant.
The protocol, in brief
| Method | Description |
|---|---|
TAP |
Opens/closes a session. Must precede POUR. POST accepted but STRONGLY DISCOURAGED. |
POUR |
Dispenses beer (start/stop). MUST NOT execute without open TAP session. |
WHEN |
Stops foam. Inherited from HTCPCP. There is no WHEN-WHEN method. Once is sufficient. |
GET |
Returns tap state. Contains no beer. This is an important distinction. |
PROPFIND |
Discovers styles, temperatures, foam levels. Borrowed from WebDAV via HTCPCP. |
New error code: 419 I'm a Wine Glass. A wine glass is not a mug. A wine glass has no handle. Beer poured into a wine glass loses carbonation 23% faster β a figure the authors have not verified but feel is directionally correct.
A server MUST NOT return 419 for an empty keg. The keg is not a wine glass. These are different problems.
Demo
πΊ HTBMCP Simulator β open it directly in your browser. No install, no server, no dependencies. A complete HTBMCP state machine running client-side: animated tap visuals, all six response codes, the full header set.
Try to TAP a Stout at 3Β°C. Watch the 406 fire with the exact RFC citation. Try the wine glass tab. Say WHEN.
Attempting to pour into a wine glass results in a protocol-level error.
For the full server:
python server.py
# πΊ HTBMCP/1.0 listening on 0.0.0.0:1414
# Port 1414 β memorial: Gdansk municipal archives, 1414
# TAP open
curl -X TAP http://localhost:1414/tap/tap-1 \
-H "Content-Type: message/mugpot" \
-H "Accept-Style: IPA" \
-H "Accept-Temperature: 8" \
-H "Accept-Foam: normal" \
-d "open"
# POUR
curl -X POUR http://localhost:1414/tap/tap-1/pour \
-H "Content-Type: message/mugpot" \
-d "start"
# WHEN β enough foam
curl -X WHEN http://localhost:1414/tap/tap-1/when
# 419 β I'm a Wine Glass
curl -X POUR http://localhost:1414/wine-glass/tap-1
Code
Code is on Github repository: https://github.com/pcescato/htbmcp/
The full implementation β TCP server, FastAPI test app, registry, 41 tests, browser simulator, and README. RFC 1516 itself is available in the repository. It will be formally published on April 1st, 2026. As tradition demands.
The uvicorn problem β and the solution
This is the key technical lesson, and it's worth making explicit.
# β This will NOT work for HTBMCP methods
# uvicorn β h11 β validates method against IANA registry β rejects TAP/POUR/WHEN
# before any application code runs
uvicorn main:app --reload
The fix β a minimal HTTP/1.1 parser over raw asyncio TCP:
async def handle_connection(reader, writer):
data = await reader.read(8192)
req = parse_request(data) # accepts any RFC 7230 token as method
response = await dispatch(req)
writer.write(response)
await writer.drain()
async def main():
server = await asyncio.start_server(handle_connection, "0.0.0.0", 1414)
async with server:
await server.serve_forever()
parse_request() splits on the first space and takes whatever token it finds β TAP, POUR, WHEN, or anything else that's RFC 7230-valid. No registry check. This is correct: HTBMCP defines its own protocol. The transport layer should not enforce HTTP's method vocabulary on a protocol that extends it.
Temperature validation β MUST vs SHOULD NOT
def check_temperature(tap_id, raw_temp):
# RFC 1516 Β§3.2.3:
# "A server MUST NOT serve a Stout at 3Β°C.
# This is not a SHOULD NOT. This is a MUST NOT.
# The authors feel strongly about this."
if tap.style == "Stout" and temp <= 5:
return build_response(406, {
"error": "Temperature violation β MUST NOT",
"detail": "A server MUST NOT serve a Stout at 3Β°C. "
"This is not a SHOULD NOT. This is a MUST NOT.",
"allowed_range": "10β13Β°C",
"rfc": "RFC 1516 Β§3.2.3",
})
The RFC distinguishes MUST NOT from SHOULD NOT with care. The Stout temperature constraint is a hard rule, not a recommendation. Testing that distinction is the point:
def test_tap_406_stout_too_cold(self):
"""RFC 1516 Β§3.2.3: MUST NOT. This is not a SHOULD NOT."""
r = self._tap_open(tap_id="tap-2", style="Stout", temp=3)
assert r.status_code == 406
assert "MUST NOT" in r.json()["error"]
The piwo:// tap β port 1414's justification, made navigable
"tap-gdansk": Tap(
id="tap-gdansk", scheme="piwo", # Polish β RFC 1516 Β§1, port 1414 memorial
style="Lager", temp=4.0, pressure=2.6, level=88,
compatible_styles=["Lager", "Pilsner"],
),
def test_gdansk_piwo_tap(self):
"""piwo:// scheme β Gdansk 1414. We pour one out."""
r = self._tap_open(tap_id="tap-gdansk", style="Lager", temp=4)
assert r.status_code == 200
assert TAP_REGISTRY["tap-gdansk"].scheme == "piwo"
How I Built It
Stack: Python 3.12, FastAPI (test suite only), asyncio raw TCP (real server), structlog, pytest. Single-file HTML/JS simulator with no dependencies.
Build order: simulator first, server second, tests throughout. This is the lesson from the HTCPCP implementation: build the thing you can demo immediately, then build the thing that's correct. The simulator forced every protocol decision to be made as UI state before it became a routing decision.
The protocol is not just simulated β it is fully implemented and validated with a dedicated test suite.
The HTCPCP comparison: HTBMCP adds a session model (TAP as initiator with brew_version tokens for concurrent access), promotes foam to a first-class protocol feature (Accept-Foam with five levels including belgian β implementation-defined, but significant), and introduces 419 I'm a Wine Glass alongside 418 I'm a teapot. Where 418 says you are the wrong device, 419 says you are using the wrong vessel. The error taxonomy matters.
What this actually teaches: The Stout MUST NOT forces you to think about hard constraints vs recommendations. The brew_version conflict detection forces you to think about optimistic locking. The goblet Trappist-only rule forces you to think about domain validation. The uvicorn rejection forces you to think about where the HTTP stack lives. These are not beer problems.
The Reinheitsgebot requires no acknowledgement. It has been enforcing standards compliance since 1516 without asking for credit.



Top comments (5)
hahaha perfect! Now itβs not only me whoβll be famous β but GdaΕsk too! π
Sylwia, you were already famous here. And GdaΕsk was already famous for 1414. Now it has a port number too. πΊ
Port 1414 is a memorial as much as a transport binding β that's in the RFC. Your city lost that document to the war. We pour one out, and we make sure the protocol remembers.
Thank you for the suggestion that started all this. The Acknowledgements section of RFC 1516 has your name. As it should.
418 is love, 418 is life. π Loving the HTBMCP tease β if that becomes a real RFC, I'm filing a PR for the 'Handle with Beer' middleware. Cheers to community builds that keep the web weird and wonderful! πΊπ₯
418 is indeed the foundation on which all serious distributed systems are built. πΊ
"Handle with Beer" middleware is exactly the kind of contribution RFC 1516 was designed to encourage β PRs will be open on April 1st. The merge criteria: it must compile, it must pour, and it must respect the Stout temperature constraint. MUST NOT, not SHOULD NOT.
Glad to have you in the community that keeps the web weird. π₯
Awesome!