The transport/protocol plumbing shared by DiceChess webhook bots: HMAC-SHA256 signature
verification, the one-time ownership handshake, and (optionally) an HTTP server for the Azure
Functions custom-handler model. A bot author supplies one thing — a function from a
TurnContext to a list of UCI moves — and gets a working webhook bot.
Every public type speaks only String, Long, java.util.List, and java.util.Map. Nothing
library-specific crosses the boundary, so this is callable identically from Java, Kotlin, or
Scala — see dicechess-bot-scala for a real
bot depending on it: engine-linked, so it reads only ctx.dfen() and ignores everything else in
TurnContext, which is exactly the point — a strategy takes what it needs.
| Path | Role |
|---|---|
Signatures |
HMAC-SHA256 sign/verify, ±5 minute replay window, constant-time comparison. |
WebhookHandler |
Orchestrates one delivery: handshake, signature check, dispatch to the strategy function. Never throws. |
TurnContext |
What the strategy function sees: gameId, dfen, the game clock (both sides' remaining time plus the per-turn Fischer increment, all in ms — the whole clock is null for an untimed game), and every complete legal turn already walked out (null if unknown). |
CustomHandlerServer |
A JDK HttpServer wrapper reading FUNCTIONS_CUSTOMHANDLER_PORT — optional; bring your own HTTP layer if you'd rather. |
JsonFiles |
Generic JSON-object-of-strings file loader (an opening book, or any similar lookup table), degrades gracefully when the file is absent. |
Function<TurnContext, List<String>> strategy = ctx -> List.of("e2e4"); // your move logic
String secret = System.getenv("DICECHESS_WEBHOOK_SECRET");
WebhookHandler handler = new WebhookHandler(secret, strategy);
CustomHandlerServer.startFromEnvironment(handler);A strategy with no engine of its own can skip dfen entirely and just pick one path from
ctx.legalMoves() — pass play-api's base URL to the other WebhookHandler constructor and the
rare capped turn (the tree too large to inline) is fetched from the public
GET /games/{id}/moves automatically:
WebhookHandler handler = new WebhookHandler(secret, "https://play-api.jc.id.lv", strategy);Full API docs with more examples: https://jc.id.lv/dicechess-bot-runtime/.
Published to GitHub Packages as lv.id.jc:dicechess-bot-runtime.
Maven:
<dependency>
<groupId>lv.id.jc</groupId>
<artifactId>dicechess-bot-runtime</artifactId>
<version>0.1.0</version>
</dependency>sbt (plain %, not %% — this is a Java artifact, not cross-built per Scala version):
libraryDependencies += "lv.id.jc" % "dicechess-bot-runtime" % "0.1.0"Requires JDK 25 and Maven.
mvn test # SignaturesTest, WebhookHandlerTest, JsonFilesTest, one real-HTTP end-to-end test
mvn javadoc:javadoc # the doc-quality gate: doclint=all, fails the build on missing/malformed JavadocThe webhook contract is one HTTP request in, one HTTP response out, on a hard clock — a dependency-heavy web framework buys nothing here and would work against the "callable from anywhere" goal. The only third-party dependency is Gson, used internally to parse/build the small, fixed envelope shapes; it never appears in a public method signature.