docs(crafty): "You are not invited" is an empty allowlist -- and it proves RakNet works
New operator observation: a Bedrock client got "Unable to connect / You are not invited to play on this server". That was put to me as evidence that NetherNet signalling is working, that the refusal may have come from Microsoft rather than our server, and that BDS players therefore never traverse crafty-l4-udp. Measured read-only at 21:56, all three are wrong. allow-list=true (server.properties line 37) allowlist.json == [] (3 bytes, mtime 20:51, never edited since create) online-mode=true transport=raknet (line 71; file mtime STILL 21:33:31) UDP 19132 bound (/proc/net/udp 00000000:4ABC, pid 47006) TCP 19132 ABSENT (no 4ABC in /proc/net/tcp or tcp6) bedrock_server pid 47006, same process since 21:34, never restarted 1. transport is still raknet -- nobody reverted it, the binary did not rewrite it (mtime unchanged, audit.log records no later edit, same pid). NetherNet is NOT in use and cannot be what served this client. 2. NetherNet could not have served it in principle: it signals over TCP on server-port, and there is no TCP 19132 listener in the pod at all. 3. So the refusal came from OUR server, over RakNet, over 19132/udp, through the VIP. The 19132-19134/UDP exposure is the opposite of inert -- it carried both the earlier pong and this refusal. 4. Cause is certain and trivial: allow-list=true with an empty allowlist.json refuses every player. The shipped how_to says so, and it keys on Xbox Gamertag. The requested log correlation cannot be done from disk: K3s-Bedrock's log_path in crafty.sqlite is the empty string and Dedicated_Server.txt is 0 bytes -- Crafty persists no BDS stdout at all, the console is panel-UI-only. commander.log 21:39-21:56 holds only homelab crash-detection and raknet_ping lines. That silence is evidence about Crafty's logging, not about the refusal's origin. Adds a README section on the join authorization model: the measured table, the proof the refusal is ours, the "console is not on disk" caveat, gamertag/XUID identity, and three operator routes (console `allowlist add`, edit-then-`allowlist reload`, or allow-list=false -- discouraged, since the game port is deliberately un-gated so the allowlist is the only join control). Notes allowlist.json is PVC state that resets to [] on a server recreate. Options table and human test 4 now check the allowlist before blaming the network. Ranking unchanged at the top: keep transport=raknet, add gamertags. Still zero manifest change. NetherNet would ADD work (TCP 19132 in crafty-l4-tcp and in crafty-allow-ingress, plus server-udp-ports) and admit not one extra player. Read-only throughout: allowlist.json, server.properties and the DB were read; no console command issued, no kubectl mutation, no flux reconcile. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
1 parent
8697702d51
commit
cd2617e5b3
2 files changed
+207
-5
No files matched your search
@@ -5503,3 +5503,120 @@ not merge it.**
|
||||
then re-ping. The vendor default is `nethernet`, and the regression is silent.
|
||||
6. **If a second simultaneous player evicts the first**, that is the SNAT/UDP-conntrack theory becoming
|
||||
real; it needs its own measured investigation, not a guess.
|
||||
|
||||
## 2026-09-29 (later still) — "You are not invited": the join gate is an empty allowlist. RakNet is confirmed working *by the refusal itself*
|
||||
|
||||
A Bedrock client reported **"Unable to connect / You are not invited to play on this server"**. This was
|
||||
put to me as evidence that NetherNet signalling is working, that the refusal might have come from
|
||||
Microsoft rather than our server, and that BDS players therefore never traverse this repo's
|
||||
`crafty-l4-udp` path. **Measured, all three of those are wrong.** The refusal is ours, it came back over
|
||||
`19132/udp`, and it has a one-line cause.
|
||||
|
||||
### Measured 21:56 (read-only)
|
||||
|
||||
| Property / file | Value |
|
||||
|---|---|
|
||||
| `allow-list` | **`true`** (line 37 of `server.properties`) |
|
||||
| `allowlist.json` | **`[]`** — 3 bytes, mtime **20:51**, never edited since the server was created |
|
||||
| `online-mode` | `true` |
|
||||
| `transport` | **`raknet`** (line 71); `server.properties` mtime **still 21:33:31** |
|
||||
| UDP 19132 | bound — `/proc/net/udp` `00000000:4ABC`, pid 47006 |
|
||||
| **TCP 19132** | **absent** — `grep 4ABC /proc/net/tcp /proc/net/tcp6` → no match |
|
||||
| `bedrock_server` | pid **47006**, etime 21:58 — the *same* process since 21:34, never restarted |
|
||||
| `K3s-Bedrock.log_path` | **`''`** (empty string) in `crafty.sqlite`; `Dedicated_Server.txt` is **0 bytes** |
|
||||
|
||||
### What that settles
|
||||
|
||||
1. **`transport` is still `raknet`. Nobody reverted it and the binary did not rewrite it.** The file's
|
||||
mtime is unchanged from the operator's 21:33:31 edit, `audit.log` records no later edit, and pid
|
||||
47006 has been the same process since 21:34. **So NetherNet is NOT in use and cannot be what served
|
||||
this client.** The premise that "something is evidently set to nethernet" is refuted.
|
||||
2. **NetherNet could not have served it even in principle:** nethernet signals over a **TCP** socket on
|
||||
`server-port`, and there is **no TCP 19132 listener in the pod at all**. No signalling endpoint
|
||||
exists, and none is exposed by any Service.
|
||||
3. **Therefore the refusal came from our server, over RakNet, over `19132/udp`, through the VIP.** A
|
||||
refusal authored by the server requires the client to have reached the server; the only listener is
|
||||
the RakNet UDP socket. **The 19132-19134/UDP exposure is the opposite of inert — it is the path that
|
||||
carried both the earlier pong and this refusal.** *(Observed: the listeners, the missing TCP socket,
|
||||
`transport=raknet`, the process identity. Reasoned from those: that the refusal traversed that path.)*
|
||||
4. **The cause is trivial and certain: `allow-list=true` with `allowlist.json == []` refuses every
|
||||
player.** The shipped `bedrock_server_how_to.html` states it — *"If the `allow-list` property is
|
||||
enabled … the server will only allow selected users to connect. To allow a user to connect you need
|
||||
to know their Xbox Live Gamertag."* An empty array selects nobody. **This configuration cannot admit
|
||||
anyone, so the refusal needed no other explanation.**
|
||||
|
||||
### The correlation I was asked for, and why it cannot be done from disk
|
||||
|
||||
I was asked to correlate the client error against the BDS server log and `commander.log` at 21:40–22:00,
|
||||
on the basis that our log showing nothing would mean the refusal came from Microsoft. **That inference
|
||||
does not hold here, because there is no BDS log on disk to be silent.** ``K3s-Bedrock``'s `log_path` is
|
||||
the empty string and `Dedicated_Server.txt` is 0 bytes: **Crafty persists no Bedrock stdout anywhere on
|
||||
the PVC** — the console is panel-UI-only. `commander.log` 21:39–21:56 holds nothing but `homelab`
|
||||
crash-detection lines and `raknet_ping` chatter: no join, no gamertag, no XUID, no allowlist denial.
|
||||
That silence is evidence about Crafty's logging, **not** about the refusal's origin. **UNVERIFIED and
|
||||
only observable live in the panel console.** The point stands: absence of a log here is not absence of
|
||||
an event.
|
||||
|
||||
I also did **not** capture the player's packet (no `tcpdump`, no `cilium monitor`), so the flow's
|
||||
identity is inferred from the listener topology, not seen. What removes the doubt is that the
|
||||
configuration is *unconditionally* refusing: whichever path a client takes, `allow-list=true` plus `[]`
|
||||
denies it. The operator action is required either way.
|
||||
|
||||
### Correction to the earlier entry's loose end, now firmer
|
||||
|
||||
The previous entries left "who sent those 26 packets / 27660 bytes on the `19132/UDP` policy map"
|
||||
UNVERIFIED, and it stays UNVERIFIED — but note it was measured *before* 21:34, i.e. during the nethernet
|
||||
window when **no UDP listener existed**. Those packets are consistent with clients RakNet-pinging a port
|
||||
nothing was listening on. They should **not** be attributed to a successful Bedrock session, and equally
|
||||
should not now be re-attributed to this refused client, which arrived later.
|
||||
|
||||
### Ranking after this measurement — unchanged at the top
|
||||
|
||||
**Keep `transport=raknet`, add the players to the allowlist. Still zero manifest change.** The allowlist
|
||||
refusal is orthogonal to transport: it would refuse identically under NetherNet. Switching to NetherNet
|
||||
would *add* manifest work (TCP 19132 in `crafty-l4-tcp` **and** `crafty-allow-ingress`, plus
|
||||
`server-udp-ports` pinned and exposed) and would admit **not one extra player**. "Accept NetherNet" does
|
||||
**not** outrank the status quo on this evidence, because NetherNet is not what is working — RakNet is.
|
||||
Geyser stays at #2, reserve-only.
|
||||
|
||||
### What changed in this repo
|
||||
|
||||
`platform/crafty/README.md` — added *"You are not invited to play on this server — the join gate is the
|
||||
allowlist, not the network"*: the measured table, the proof that the refusal is ours and rode the UDP
|
||||
path, the "BDS console is not on disk" caveat, the gamertag/XUID identity model, the three operator
|
||||
routes (`allowlist add` in the console; edit-then-`allowlist reload`; or `allow-list=false`, discouraged
|
||||
because the game port is deliberately un-gated so the allowlist is the *only* join control), and a note
|
||||
that `allowlist.json` is PVC state that resets to `[]` on a server recreate. Updated the options table
|
||||
and human test 4 to put the allowlist check before blaming the network. **No manifest touched.**
|
||||
|
||||
### Still UNVERIFIED
|
||||
|
||||
- **No successful join.** Reachability and an authorization refusal are both proven; a completed login is
|
||||
not. Only a human with a client, after an allowlist entry, can close it.
|
||||
- **The exact client-side string was not observed by me** — it is the operator's report, and its mapping
|
||||
to the allowlist denial is **reasoned** from the vendor doc, not read out of a BDS log (there is none).
|
||||
- **Whether the client used Add-Server-by-IP or an Xbox/Friends route is unknown**, so a *concurrent*
|
||||
Xbox-side "not invited" cannot be fully excluded. It does not matter operationally: the allowlist must
|
||||
be fixed regardless, and after that a retry distinguishes the two immediately.
|
||||
- **The multi-player SNAT question** remains open and untested.
|
||||
- **Nothing was written.** `allowlist.json`, `server.properties` and the DB were read only; no
|
||||
`allowlist add` was issued, no console command sent, no `kubectl` mutation, no `flux reconcile`.
|
||||
|
||||
### Operator actions — revised, allowlist first
|
||||
|
||||
1. **Add each player's Xbox Gamertag to the allowlist.** Crafty → `K3s-Bedrock` → Console:
|
||||
`allowlist add <Gamertag>` (quote if it contains a space). Takes effect immediately, no restart.
|
||||
Verify with `allowlist list`. This is **the** fix for "You are not invited".
|
||||
2. **Then retry the join** from a Windows/mobile client: **Add Server** →
|
||||
`crafty-mc.home.keithjackson.dev`, port **19132**. If it now connects, everything in these three
|
||||
entries is closed. If it still refuses, capture the **panel console** output during the attempt —
|
||||
that is the only place BDS's own reason is visible.
|
||||
3. **Do not set `allow-list=false`** as the fix. The game port is deliberately un-gated at the network
|
||||
layer, so the allowlist is the only thing deciding who may join. (And console `allowlist off` does not
|
||||
change `server.properties`, so it silently reverts on restart.)
|
||||
4. **Leave `transport=raknet` alone**, and keep ignoring both the `TRANSPORT TYPE ERROR` banner and the
|
||||
`too few fields` warning — the server is reachable, proven by pong and by this refusal.
|
||||
5. **`auto_start=0` / `crash_detection=0` on `K3s-Bedrock` — still outstanding.** `PATCH
|
||||
/api/v2/servers/c5837301-12b1-404e-b50e-70eafb37226b`, both true, **read them back**.
|
||||
6. **Treat `allowlist.json` and `transport=raknet` as backup-relevant PVC state.** A server recreate
|
||||
resets the allowlist to `[]` and the transport to `nethernet` — both silently.
|
||||
@@ -181,6 +181,89 @@ one `None` terminator entry as missing. Every field that matters (edition, MOTD,
|
||||
name, player count, max players, uuid, game mode, both ports) parsed correctly. **Do not chase this
|
||||
warning.** The failure signature is the *other* message, `Got timeout`.
|
||||
|
||||
### "You are not invited to play on this server" — the join gate is the allowlist, not the network (MEASURED 2026-09-29 21:56)
|
||||
|
||||
A Bedrock client reported **"Unable to connect / You are not invited to play on this server"**. That is
|
||||
an **authorization** refusal, and it is the expected, guaranteed behaviour of this server's current
|
||||
configuration. Measured:
|
||||
|
||||
| Property / file | Value | Source |
|
||||
|---|---|---|
|
||||
| `allow-list` | **`true`** (line 37) | `server.properties` |
|
||||
| `allowlist.json` | **`[]`** — 3 bytes, mtime 20:51, never edited since server create | `cat allowlist.json` |
|
||||
| `online-mode` | `true` (line 31) | `server.properties` |
|
||||
| `transport` | **`raknet`** (line 71), file mtime **still 21:33:31** | `server.properties` |
|
||||
| TCP 19132 listener | **none** (`NO-TCP-4ABC`) | `grep 4ABC /proc/net/tcp /proc/net/tcp6` |
|
||||
| UDP 19132 listener | present, `00000000:4ABC`, pid 47006 | `grep 4ABC /proc/net/udp` |
|
||||
|
||||
**`allow-list=true` with an empty `allowlist.json` refuses every player, unconditionally.** The shipped
|
||||
`bedrock_server_how_to.html` states it: *"If the `allow-list` property is enabled in server.properties
|
||||
then the server will only allow selected users to connect. To allow a user to connect you need to know
|
||||
their Xbox Live **Gamertag**."* An empty list selects nobody. This is not a network problem, not a
|
||||
transport problem, and not a platform problem — and no amount of Service or policy work will change it.
|
||||
|
||||
**This is also positive evidence that the RakNet path works end to end.** A refusal authored by *our*
|
||||
server required the client to reach our server and get an answer back. The only listener on 19132 is
|
||||
the **RakNet UDP** socket — there is **no TCP 19132 listener at all**, and NetherNet requires a TCP
|
||||
signalling socket on `server-port`. So the client cannot have used NetherNet against this server; it
|
||||
arrived over `19132/udp`, through `crafty-l4-udp` and the VIP. **The 19132-19134/UDP exposure is not
|
||||
inert — it is the path that carried both the pong and this refusal.** *(Observed: the listeners, the
|
||||
absent TCP socket, `transport=raknet`. Reasoned from those: that the refusal traversed that path. Not
|
||||
captured: the actual player flow — see the caveat below.)*
|
||||
|
||||
#### Caveat: BDS console output is NOT on disk, so a join attempt cannot be correlated from the filesystem
|
||||
|
||||
`K3s-Bedrock`'s `log_path` in `crafty.sqlite` is the **empty string**, and `Dedicated_Server.txt` is
|
||||
**0 bytes**. Crafty does not persist a Bedrock server's stdout anywhere on the PVC — the console exists
|
||||
only in the panel UI. `commander.log` between 21:39 and 21:56 contains nothing but `homelab`
|
||||
crash-detection lines and the `raknet_ping` chatter: **no join, no gamertag, no XUID, no allowlist
|
||||
denial.** That absence is **not** evidence about where the refusal came from; it is evidence that
|
||||
Crafty logs no BDS stdout at all. **To see a rejected join you must watch the panel console live.**
|
||||
UNVERIFIED, and only observable in the UI.
|
||||
|
||||
#### How a Bedrock player actually gets authorized here
|
||||
|
||||
Identity is an **Xbox Live Gamertag** (`online-mode=true` forces XBL auth, which is what gives BDS a
|
||||
gamertag/XUID to check). `allowlist.json` is a JSON array; per the vendor doc the keys are `name` (the
|
||||
gamertag), optional `xuid` (*"populated when someone with a matching name connects"*), and
|
||||
`ignoresPlayerLimit`. So the operator adds **one entry per player, keyed on gamertag** — there is no
|
||||
group, role, or Keycloak integration, and **none of this touches the oauth2-proxy/Keycloak gate**,
|
||||
which protects only the web panel.
|
||||
|
||||
Three ways to do it, all **operator actions**:
|
||||
|
||||
1. **Preferred — the panel console.** In Crafty, open `K3s-Bedrock` → Console and send:
|
||||
`allowlist add <Gamertag>` (quote it if it contains a space: `allowlist add "Example Name"`).
|
||||
This writes `allowlist.json` **and** takes effect immediately, with no restart.
|
||||
2. **Edit the file, then reload.** Write `/crafty/servers/c5837301-…/allowlist.json`, then send
|
||||
`allowlist reload` in the console — *"After you've modified the file you need to run the command
|
||||
`allowlist reload` to make sure that the server knows about your new change."* Shape:
|
||||
|
||||
```json
|
||||
[
|
||||
{ "ignoresPlayerLimit": false, "name": "FirstPlayerGamertag" },
|
||||
{ "ignoresPlayerLimit": false, "name": "SecondPlayerGamertag" }
|
||||
]
|
||||
```
|
||||
|
||||
3. **Turn the allowlist off entirely** — `allow-list=false` in `server.properties` (restart), or
|
||||
`allowlist off` in the console. Note the vendor's warning: `allowlist on|off` *"does not change the
|
||||
value in the server.properties file"*, so a console `off` silently reverts on the next start.
|
||||
**Not recommended:** the game port is deliberately un-gated at the network layer (see *Game-port L4
|
||||
exposure*), so the allowlist is the **only** thing deciding who may join. Keep it `true` and enumerate
|
||||
players.
|
||||
|
||||
> **`allowlist.json` is application state on the PVC.** It is not in git, Flux cannot restore it, and a
|
||||
> server recreate resets it to `[]` — i.e. back to refusing everyone. Same class of problem as
|
||||
> `transport=raknet`. Treat both as backup-relevant.
|
||||
|
||||
#### Why this does not change the transport recommendation
|
||||
|
||||
`transport=raknet` is still correct and still the recommendation. The allowlist refusal is orthogonal —
|
||||
it would refuse players identically under NetherNet. Switching to NetherNet would *add* work (TCP 19132
|
||||
in `crafty-l4-tcp` **and** in `crafty-allow-ingress`, plus `server-udp-ports` pinned and exposed) and
|
||||
would not admit a single extra player. The ranking below stands, with the allowlist as the live blocker.
|
||||
|
||||
#### Decision: `transport=raknet` stays; the 19132-19134/UDP exposure is correct and live
|
||||
|
||||
No manifest change is needed or wanted. `service-l4.yaml`, `networkpolicy.yaml` and the
|
||||
@@ -190,11 +273,11 @@ recommended** today:
|
||||
|
||||
| Option | Manifest consequence | Verdict |
|
||||
|---|---|---|
|
||||
| **Keep `transport=raknet`** (recommended) | **none** — the existing Service, policy and ports are already correct and now proven | **Take this.** Zero change, works today, vanilla parity, Bedrock clients join a real Bedrock world. |
|
||||
| **Keep `transport=raknet`** (recommended) | **none** — the existing Service, policy and ports are already correct and now proven end-to-end | **Take this.** Zero change, reachable today, vanilla parity, Bedrock clients join a real Bedrock world. The remaining blocker is the **allowlist**, not the transport (above). |
|
||||
| **GeyserMC + Floodgate** on the `homelab` Paper server | none either (Geyser also binds `19132/udp`), but it would **collide** with the live BDS on 19132 — one of them must move | Hold in reserve. Only worth it if RakNet is really removed, or if you *want* one shared world. Cost: Bedrock players join the **Java** world, not a Bedrock world; Java-only mechanics and some content differ; it is another moving part to patch. |
|
||||
| **Pin/downgrade BDS** to a pre-nethernet release | none | Unnecessary — the *current* build already speaks RakNet. Also fragile: Microsoft removes old BDS downloads, and Crafty's Bedrock installer tracks latest. UNVERIFIED whether Crafty can pin a version at all. |
|
||||
| **A third-party server** (PocketMine-MP, Nukkit) | none | No. Loses vanilla parity and the world format; a large change to solve a problem that is not occurring. |
|
||||
| **Accept NetherNet** | 19132-19134/UDP becomes **inert**; nethernet would instead need **TCP** 19132 added to `crafty-l4-tcp` *and* to `crafty-allow-ingress`, plus `server-udp-ports` pinned and exposed for the negotiated media path | No. Strictly more manifest work than the option that already works, and the WebRTC media path behind a shared VIP and SNAT is unproven here. |
|
||||
| **Accept NetherNet** | 19132-19134/UDP becomes **inert**; nethernet would instead need **TCP** 19132 added to `crafty-l4-tcp` *and* to `crafty-allow-ingress`, plus `server-udp-ports` pinned and exposed for the negotiated media path | No — and note it is **not currently in use**: `transport=raknet` and there is no TCP 19132 listener. Strictly more manifest work than the option that already works, it admits no extra player (the allowlist is the gate), and WebRTC media behind a shared VIP and SNAT is unproven here. |
|
||||
|
||||
#### If RakNet is ever really removed
|
||||
|
||||
@@ -267,9 +350,11 @@ Gateway VIP). The sidecar→Crafty hop is pod-local (`127.0.0.1`) and not subjec
|
||||
pong from off-cluster. **Ignore the `TRANSPORT TYPE ERROR` banner in the console and ignore
|
||||
Crafty's `too few fields` warning — both appear on a fully working server.** The authoritative
|
||||
test is a RakNet Unconnected Ping to `192.168.2.22:19132` returning a `0x1c` packet whose MOTD
|
||||
string starts `MCPE;`. 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.
|
||||
string starts `MCPE;`. **Then check the allowlist** — `allow-list=true` with `allowlist.json == []`
|
||||
refuses every player with *"You are not invited to play on this server"* even though the server is
|
||||
fully reachable (see *the join gate* above); add the player's **Xbox Gamertag** first. 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
|
||||
|
||||
|
||||
Reference in new issue
Block a user