Farmstead Mods
RU

Dashboard connectivity

Internal and external modes, webDashboard.mode and troubleshooting connection problems.

On this page

Examples#

Tell "port unreachable" from "key rejected"#

The external panel cannot connect, and both cases look like a timeout.

/dashboard status

From the machine running the panel: curl http://<ip>:8080/api/ping. A 200 with {"success":true,"mod":"neoessentials","mode":"..."} means HTTP is alive — next, check pairing/the API key. Connection refused means a firewall, the bind address, or the dashboard did not start. /dashboard status in game shows whether the built-in HTTP server is up.

API only, without the built-in UI#

You run the Laravel NeoEssentials-Dashboard, do not need the built-in page on :8080, and it spams the log about a missing index.html.

/dashboard status

webDashboard.mode: "external" (the default) registers only /api/*. Changing the mode requires a restart. internal and both also serve the UI bundle at / — for that the UI must be packaged into the jar.

Edge cases#

mode is read at startup#

Like modules, webDashboard.mode does not change on /neoe reload. GET /api/ping needs no authorization and is registered in every mode — it is the probe for "is the port alive".

What to do: Changed the mode — restart. Diagnose the network with ping, not by logging in to the panel.

Known issues#

On old builds /dashboard start was dead code#

The class existed but was not registered in the dispatcher — the command was "unknown". In this branch /dashboard is wired up; if the server runs an ancient jar, status/start will be unknown regardless of the config.

What to do: Check the mod version. On the current fork the command should appear in /help when the module is enabled.

Detailed guide#

The section below is the project's original documentation (docs/Wiki/DashboardConnectivity.md). Links to other wiki pages are rewritten to this site; the guides themselves are not edited, so upstream syncs stay conflict-free.

Covers connecting a separately-hosted dashboard app (e.g. NeoEssentials-Dashboard, the Laravel app) to the mod's built-in API, diagnosing "the connection isn't establishing", and the webDashboard.mode config option ("external" vs. the mod's own bundled "internal" UI).


The two hosting modes#

The mod's dashboard has always been two things bundled together: 1. A REST API under /api/* (moderation, players, economy, permissions, etc.) 2. A static UI served at / — the mod's own bundled HTML/JS/CSS dashboard frontend

Until now there was no way to run just the API without the mod also trying (and, in most dev/ production setups, failing) to serve its own bundled UI — every boot logged Dashboard resources NOT found - /webdashboard/index.html is null! even on servers that only ever use an external dashboard app and never load http://<server>:8080/ directly.

Config: config.json → webDashboard.mode#

Value Behavior
"external" (default) Serves only /api/*. The / static-file route is never registered. Use this when a separate app (Laravel NeoEssentials-Dashboard) is the only thing that ever talks to this port — the shipped config template sets this explicitly.
"internal" Serves the mod's own bundled dashboard UI at / and the API at /api/* — no separate app to install. As of build.13 this bundled UI is feature-complete (all 13 pages, matching the external app) — use this if you'd rather not run/maintain a separate Laravel process.
"both" Explicit alias for "internal" — same behavior, for config clarity when you deliberately use the built-in UI and point an external app at the same API at the same time.

Switching to "internal"/"both" requires the mod's Gradle build to have bundled the dashboard UI assets (webdashboard-ui/ → build/generated/dashboardUiResources/, wired into processResources — this happens automatically for anyone building from source; official released jars already include it).

Requires a restart to take effect (same as every other config toggle — see the "reload doesn't add/remove commands" limitation documented elsewhere).

New endpoint: GET /api/ping#

Unauthenticated, always registered regardless of mode. Returns:

{"success": true, "mod": "neoessentials", "mode": "internal"}

Use this to answer "can I reach the mod's HTTP port at all" as a separate question from "is the dashboard actually paired/authenticated" — the two failure modes look identical from the outside (both just time out or error) but need completely different fixes.

curl http://<mod-server-ip>:8080/api/ping
  • Connection refused / timeout → network problem (see checklist below), not a mod bug.
  • 200 with the JSON above → the mod's API is reachable; if the dashboard app still can't connect, the problem is in the pairing/API key, not connectivity — see API System → Pairing.

Historical root cause (already fixed, worth knowing about if you're on an old build)#

The most common reason "the connection isn't establishing" used to come up at all: /dashboard start/stop/status/restart were dead code — the class existed and compiled, but was never actually registered with the command dispatcher, so they were literally unknown commands. Combined with webDashboard.autoStart: false, this meant the dashboard's HTTP server could simply never be running at all, with no in-game way to diagnose or fix it — no amount of correct MC_API_URL/ service-account config on the external dashboard's side would help, since nothing was listening on the port in the first place. Fixed long since; only relevant if you're troubleshooting a very old build.

Action item for anyone hitting this: check /dashboard status in-game (after restarting to pick up the fix) — if it says stopped, either set autoStart: true or run /dashboard start.


Full diagnostic checklist for "external dashboard can't connect to the mod"#

Work through these in order — each rules out one layer:

  1. Is the mod's dashboard actually running? /dashboard status in-game. If stopped: /dashboard start, or set webDashboard.enabled / modules.webDashboardEnabled / webDashboard.autoStart to true and restart.

  2. Can you reach the port from the SAME machine the Minecraft server is on? curl http://127.0.0.1:8080/api/ping on the Minecraft server's own machine. If this fails, it's not a network issue — go back to step 1, or check webDashboard.port for a typo/conflict with another service.

  3. Can you reach the port from the machine the Laravel app runs on? curl http://<minecraft-server-ip>:8080/api/ping from the Laravel app's machine (or wherever MC_API_URL points). If this fails but step 2 succeeded, it's a network-layer problem: - webDashboard.bindAddress — must be 0.0.0.0 (all interfaces) if the Laravel app is on a different machine; 127.0.0.1 only accepts connections from the same machine. - Firewall on the Minecraft server's OS (Windows Firewall, ufw, etc.) — port 8080 (and 8081 for WebSocket) need an inbound allow rule. - If the Minecraft server is behind NAT/a router (common for home-hosted servers), the port needs to be forwarded. - Cloud/VPS firewall (AWS security group, DigitalOcean firewall, etc.) if hosted in the cloud. - If accessed via a domain/reverse proxy, check that proxy's own routing/TLS config separately — /api/ping bypassing it entirely (hit the raw IP:port) isolates whether the proxy or the mod itself is the problem.

  4. Is MC_API_URL in the Laravel app's .env actually correct? Common mistake: leaving it as http://127.0.0.1:8080 when the Minecraft server is on a different machine from where Laravel runs — 127.0.0.1 from Laravel's perspective means "the Laravel server itself", not the Minecraft server. Must be the Minecraft server's real reachable IP/hostname.

  5. Is the dashboard actually paired? /api/ping succeeding but the dashboard still can't call mutating endpoints means steps 1–4 are fine. Check: - MC_SERVICE_API_KEY in the Laravel app's .env is actually populated — it's set automatically by running /dashboard pair "<dashboardUrl>" <code> (get the code from the dashboard's own Configuration page), never hand-typed. See API System → Pairing. - The paired key hasn't been revoked — /apikey list in-game shows every key's status; if it's gone, re-pair. - The key's role is at least MODERATOR if the Laravel app needs to call mutating endpoints (ban/kick/economy adjust/etc.) — VIEWER is read-only. Role is set at pairing time via /apikey create's role (pairing itself always mints an ADMIN key).

  6. Is storage.type/database connectivity a separate red herring? Not related to dashboard connectivity — don't confuse a ClassNotFoundException from the SQLite/MySQL storage backend (a dev-environment-only classloader limitation, see the Storage wiki page) with the dashboard's own HTTP connectivity. They're independent systems.


Keeping the other branches in sync#

NeoEssentials ships three parallel dev branches, one per supported Minecraft-version line — 1.21.x (1.21.1–1.21.11), 26.1.x (pinned to 26.1–26.1.2), and 26.2.x (26.2) — kept in sync by cherry-picking every feature/fix commit across all three as it lands, including all of the dashboard/connectivity work described on this page. They share one build-number counter (a GitHub Actions repo variable, BUILD_NUMBER — not a file committed to the branches) so a fix shipped on all three lands under the same build number everywhere. Everything described on this page (the webDashboard.mode config, /api/ping, the dashboard security/account-linking features) is already live on all three branches.

Since this connectivity layer is plain Java (HttpExchange/HttpServer, no net.minecraft.* types touched) and JSON config, it's historically been a low-risk, mechanical cherry-pick with no exposure to the Minecraft-version API differences that trip up other cross-branch ports (e.g. GameProfile/getProfileCache() → NameAndId/server.services().nameToIdCache(), ClickType → ContainerInput) — worth knowing if you're deciding how to port a future change here yourself. 26.1.x and 26.2.x do occasionally need a small API-shape adjustment for changes in other parts of the mod (not this connectivity layer specifically) since Mojang's official mappings rename things between Minecraft versions — see each branch's own README for its current target version.


Back to Wiki Home