docs(crafty): Bedrock 19132 refuses nothing -- BDS ships transport=nethernet
gate / gate (pull_request) Successful in 1m35s
gate / conformance (pull_request) Has been skipped

Reported as "users cannot connect to Bedrock on UDP 19132". Measured read-only
on prod; the platform delivered the packets and the server never opened the
socket.

Server `K3s-Bedrock` (c5837301-..., type=minecraft-bedrock, server_port=19132)
exists and `./bedrock_server` is alive, but /proc/net/udp and /proc/net/udp6 in
the crafty container hold NO socket on 19132 (0x4ABC) in either family -- port
19132 is bound as TCP (/proc/net/tcp6 ...:4ABC state 0A). Cause, from the
server's own server.properties: transport=nethernet, the value Microsoft's BDS
archive ships. Its inline docs: `Allowed values: "raknet" or "nethernet"`, and
against server-portv6, `This property is ignored when transport=nethernet`. NetherNet is the
WebRTC/Xbox-signalled transport and opens no RakNet UDP listener. Crafty does
not manage the property (no `transport` anywhere under /crafty/app/classes/) and
its only Bedrock probe is raknet_ping.py, hence commander.log spamming
"Got timeout while issuing bedrock ping to 127.0.0.1:19132" -- over loopback,
which indicts the server, not the network.

The path below it is sound and was measured as such: crafty-l4-udp live as
externalTrafficPolicy: Cluster with the VIP in status.loadBalancer, a non-empty
EndpointSlice, `cilium-dbg service list` showing 192.168.2.22:19132/UDP =>
10.0.0.130:19132/UDP (active) on BOTH announcing nodes, the datapath policy map
carrying `Allow Ingress reserved:world 19132/UDP` with 26 packets / 27660 bytes
already counted through it, pihole answering crafty-mc -> 192.168.2.22, and Flux
at main@sha1:20ea70d == the GitRepository artifact.

So this is not a platform or manifest defect. No manifest is touched: adding
ports or widening policy for this would be manufacturing work. The repo-side
defect is documentation -- the README told the operator that creating a server
on 19132 was sufficient.

README: add "Bedrock on UDP 19132 -- the transport trap" (the measurement chain,
and the raknet_ping warning as its signature) and "Bedrock realities that are NOT
platform defects" (no LAN/Friends discovery behind a VIP, consoles cannot enter
an address, enable-lan-visibility collides with slots 1-2, server-portv6 is not
exposed). Correct three stale claims: "the operator has not created any servers
yet" (two exist), the human test implying a started Bedrock server is reachable,
and the single-server Runtime state section (now a two-row table recording
K3s-Bedrock's auto_start=0 / crash_detection=0 -- the create-API trap, live
again, so it will not survive a pod restart).

Also measured, reported, and deliberately NOT changed: the shared VIP's two
leases sit on different nodes (crafty-l4-tcp on k3s-prod-3, crafty-l4-udp on
k3s-prod-1), so two nodes ARP-announce 192.168.2.22. Benign under `Cluster` --
both nodes carry the full active frontend set -- and the existing L0 check
l2-announced-vips-serve-from-any-node already forbids the `Local` that would make
it fatal. A "shared VIPs must co-locate" check would assert runtime lease
placement a static check cannot see.

Read-only throughout: no apply/edit/patch, no flux reconcile, no write inside
the container. Operator actions (set transport=raknet and verify the UDP bind;
PATCH auto_start; tell players to Add Server by hand) are in the DECISIONS-LOG
entry, along with what is UNVERIFIED -- chiefly that `raknet` fixes it is
reasoned from the shipped docs, not observed.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
jakman85andClaude Opus 5 committed 2026-09-29 21:20:14 -05:00
1 parent 20ea70d221
commit 07f03536b3
2 files changed
+255 -7

No files matched your search

+79 -7
View File
@@ -83,8 +83,8 @@ themselves. `external-dns` publishes `crafty-mc.home.keithjackson.dev → 192.16
### Adding more game ports (when the operator creates servers)
The operator has **not created any servers yet**; the starter set covers 3 Java + 3 Bedrock slots. To
add capacity:
As of 2026-09-29 **two** servers exist (see *Runtime state* below): Java `homelab` on 25565 and
Bedrock `K3s-Bedrock` on 19132. The starter set covers 3 Java + 3 Bedrock slots. To add capacity:
1. In the Crafty UI, create the server and bind it to a NEW port (Java TCP or Bedrock UDP).
2. Add that port to the matching Service in `service-l4.yaml` (one
@@ -93,6 +93,63 @@ add capacity:
4. (Optional) add a `containerPort` in `deployment.yaml` for documentation.
5. Commit → Flux reconciles.
### Bedrock on UDP 19132 — the transport trap (MEASURED on prod 2026-09-29)
**A Bedrock server that Crafty created, started, and reports as running can still have nothing bound
to `19132/udp`, with every layer in this repo provably correct.** This is the first thing to check
when "Bedrock players cannot connect" — before the Service, the policy, the lease or the VIP.
Measured on prod 2026-09-29 against server `K3s-Bedrock` (`c5837301-12b1-404e-b50e-70eafb37226b`,
DB `type=minecraft-bedrock`, `server_port=19132`, process `./bedrock_server` alive):
- `/proc/net/udp` and `/proc/net/udp6` inside the crafty container held **no socket on 19132**
(`0x4ABC`) at all — only ephemeral UDP sockets. Port 19132 was bound as **TCP**
(`/proc/net/tcp6`, `…:4ABC`, state `0A`/LISTEN), not UDP.
- Cause, from the server's own `server.properties`: **`transport=nethernet`** — the value Microsoft's
BDS archive *ships by default*. Its own inline docs: `# Allowed values: "raknet" or "nethernet"`,
and against `server-portv6`: `# This property is ignored when transport=nethernet and a dual-stack
socket will be opened on server-port instead`. NetherNet is the WebRTC/Xbox-signalled transport; it
**never opens a RakNet UDP listener**, so nothing binds `19132/udp` however `server-port` is set.
- Crafty does not manage this property — `transport` does not appear anywhere under
`/crafty/app/classes/` — and Crafty's *only* Bedrock reachability probe is `raknet_ping.py`, so the
panel spams `WARNING app.classes.remote_stats.raknet_ping - Got timeout while issuing bedrock ping
to 127.0.0.1:19132` every 5–30 s against a server it started successfully. **That warning is the
signature of this trap**, and it comes from `127.0.0.1` — i.e. it indicts the server, not the LAN.
- The platform path was verified sound at the same time: `crafty-l4-udp` live as
`externalTrafficPolicy: Cluster` with `status.loadBalancer.ingress[0].ip` = the VIP, a non-empty
EndpointSlice, `cilium-dbg service list` showing `<VIP>:19132/UDP → 10.0.0.130:19132/UDP (active)`
on *every* node, and the crafty endpoint's datapath policy map carrying
`Allow Ingress reserved:world 19132/UDP` **with 26 packets / 27660 bytes already counted through
it**. Real player packets reached the pod and were allowed; the application had no socket to take
them.
**Operator fix — application state on the PVC, not git and not Flux:** stop the server in Crafty, set
`transport=raknet` in `/crafty/servers/<uuid>/server.properties`, start it, and **confirm a UDP
listener on 19132 exists** (`grep 4ABC /proc/net/udp /proc/net/udp6`, or the `raknet_ping` warnings
stopping) before suspecting the network. *(That `raknet` restores the UDP bind is read from the
shipped property documentation, not yet observed on this cluster — see the DECISIONS-LOG entry of
2026-09-29.)*
### Bedrock realities that are NOT platform defects but present identically
- **LAN / Friends-tab discovery can never work here.** Bedrock discovery is a UDP broadcast on 19132
inside one L2 segment; this server sits behind a LoadBalancer VIP (and SNAT), not on the LAN, so it
will **never** appear in a client's Friends/LAN list. Players must use **Add Server** and type the
address and port by hand: `crafty-mc.home.keithjackson.dev` (or the VIP) with port **19132**.
`external-dns` publishes that name from the **-tcp** Service, so it resolves to the shared VIP and
is correct for Bedrock too — only the port differs from Java's 25565.
- **Consoles cannot reach this at all.** Switch/Xbox/PlayStation Bedrock clients have no
arbitrary-server-address field; only Windows/mobile/Fire do. Reaching it from a console needs a DNS
redirect of a featured-server hostname, which is out of scope for `service-l4.yaml`.
- **`enable-lan-visibility=true` (the shipped default) collides with Bedrock slots 1 and 2.** Under
`transport=raknet` it makes a server bind the **default** ports 19132/19133 *even when `server-port`
is set to something else* — the shipped comment says exactly that. So a second Bedrock server put on
19133 or 19134 will fight the first for 19132. Set `enable-lan-visibility=false` on every Bedrock
server after the first; LAN discovery cannot work here anyway (above).
- **`server-portv6` is not exposed and is not meant to be.** `service-l4.yaml` is IPv4 single-stack,
so the default `server-portv6=19133` is unreachable from the LAN. Harmless — but do not read
`19133` in `server.properties` as "slot 1 is in use".
## Policies
`namespace.yaml` sets only `gateway-access=shared` (platform namespace, like headlamp/homepage), so
@@ -114,14 +171,29 @@ Gateway VIP). The sidecar→Crafty hop is pod-local (`127.0.0.1`) and not subjec
local admin. A **non-platform-admins** user → **403** (authenticated but not authorized).
2. **Pod health / fsGroup:** `kubectl -n crafty get pod` Ready 2/2; Crafty writes its DB under
`/crafty/app/config` and `/crafty/servers` without EACCES.
3. **Server-create + game reach:** in the Crafty UI create a Java server bound to `25565`; start it;
connect a Minecraft client to `crafty-mc.home.keithjackson.dev:25565` (or `192.168.1.55:25565`)
from the LAN and confirm the world loads. (Bedrock: a server on `19132/udp` reached at the VIP.)
3. **Server-create + game reach (Java):** in the Crafty UI create a Java server bound to `25565`;
start it; connect a Minecraft client to `crafty-mc.home.keithjackson.dev:25565` (or
`192.168.1.55:25565`) from the LAN and confirm the world loads.
4. **Server-create + game reach (Bedrock):** creating and starting the server is **not sufficient** —
check `transport=raknet` and a live UDP listener on 19132 first (see *the transport trap* above),
then **Add Server** by hand in a Windows/mobile client at `crafty-mc.home.keithjackson.dev` port
`19132`. It will never show up in the LAN/Friends tab, and a console cannot reach it.
## Runtime state: the servers themselves are NOT in git
As of 2026-09-14 one Java server exists — **"homelab"**, Paper 26.2, TCP 25565, 2 GiB heap, autostart
on, UUID `13c7b85d-00bb-4a58-8c86-2433ca47891c`. It was created through Crafty's v2 API, and **Mojang's
As of 2026-09-29 **two** servers exist (both measured live, `select * from servers`):
| Name | Type | Port | UUID | `auto_start` |
|---|---|---|---|---|
| `homelab` | `minecraft-java` (Paper 26.2, 2 GiB heap) | TCP 25565 | `13c7b85d-00bb-4a58-8c86-2433ca47891c` | **1** |
| `K3s-Bedrock` | `minecraft-bedrock` | UDP 19132 | `c5837301-12b1-404e-b50e-70eafb37226b` | **0** ⚠ |
`K3s-Bedrock` was created 2026-09-29 20:51 and has `auto_start=0` **and** `crash_detection=0` — the
create-API trap documented at the bottom of this file, live again. **It will not come back after a pod
restart**, and merging any change to `deployment.yaml` restarts the pod (`strategy: Recreate`). PATCH
both to true and read them back.
`homelab` was created through Crafty's v2 API, and **Mojang's
EULA (<https://aka.ms/MinecraftEULA>) was accepted on the operator's behalf** to let it start; see the
DECISIONS-LOG entry of that date.