GracePress MeshPress Bridge
Small local HTTP bridge between WordPress/MeshPress and MeshCentral.
The bridge runs beside MeshCentral and uses MeshCentral's official meshctrl tool to read device groups and devices. WordPress talks to this bridge instead of trying to speak MeshCentral websocket protocol directly.
Endpoints
GET /api/healthGET /api/statusGET /api/self-testGET /api/groupsGET /api/usersGET /api/events?limit=50GET /api/serverinfoGET /api/devicesGET /api/device/:nodeIdGET /api/fleetPOST /api/push-nowPOST /api/cache/clearGET /api/link/desktop/:nodeIdGET /api/link/terminal/:nodeIdGET /api/link/files/:nodeIdGET /api/link/:mode/:nodeId?link_mode=bothGET /api/agent-links/:groupIdPOST /api/groups/createPOST /api/groups/editPOST /api/groups/removePOST /api/groups/add-userPOST /api/groups/remove-userPOST /api/invite-linkPOST /api/device/messagePOST /api/device/toastPOST /api/action
All endpoints require Authorization: Bearer TOKEN. If MESHPRESS_BRIDGE_TOKEN is empty, protected requests are rejected.
/api/action is intentionally locked down for now. It returns a clear unsupported-command response until each broad remote-command workflow is whitelisted and tested.
Provisioning and device-command endpoints require:
MESHPRESS_ALLOW_COMMANDS="1"
Without that flag the bridge will read MeshCentral state but refuse provisioning actions.
Install
cd /storage/meshcentral-workspace/bridge/meshpress-bridge
sudo ./install.sh
sudo nano /etc/gracepress/meshpress-bridge.env
pm2 restart gp-meshpress-bridge
Required Config
MESHPRESS_BRIDGE_PORT="3099"
MESHPRESS_BRIDGE_BIND="127.0.0.1"
MESHPRESS_BRIDGE_TOKEN="generated-secret"
MESHPRESS_ALLOWED_IPS=""
MESHPRESS_AUTH_WINDOW_SECONDS="900"
MESHPRESS_AUTH_MAX_FAILURES="30"
MESHCTRL_URL="wss://127.0.0.1"
PUBLIC_MESH_URL="https://mesh.yourdomain.com"
MESHCTRL_USER="admin"
MESHCTRL_PASS=""
MESHCTRL_DOMAIN=""
MESHCTRL_LOGIN_KEY_FILE="/etc/gracepress/meshcentral-login.key"
MESHCTRL_PATH=""
MESHCTRL_IGNORE_CERT="0"
MESHPRESS_LINK_HIDE=""
MESHPRESS_LINK_MODE="gotonode"
MESHPRESS_ALLOW_COMMANDS="0"
WP_DOMAIN="https://christp2p.com"
WP_API_KEY="same-token-used-in-meshpress"
MESHPRESS_PUSH_INTERVAL_SECONDS="3600"
MESHPRESS_PUSH_EVENTS="1"
If auto-detection cannot find meshctrl, set MESHCTRL_PATH to the full path of meshctrl.js.
When WP_DOMAIN and WP_API_KEY are set, the bridge pushes fleet snapshots to WordPress at startup and then every hour.
When MESHPRESS_PUSH_EVENTS is enabled, selected bridge lifecycle events are posted to MeshPress so WordPress can record them through OmniLog.
Public Exposure Hardening
Do not expose port 3099 to the public internet without firewall/VPN controls. Preferred options:
- Bind to localhost and proxy privately:
MESHPRESS_BRIDGE_BIND="127.0.0.1"
- Bind to a Tailscale address only:
MESHPRESS_BRIDGE_BIND="100.x.y.z"
- If the bridge must listen on all interfaces temporarily, restrict source IPs at both the firewall and app layer:
MESHPRESS_BRIDGE_BIND="0.0.0.0"
MESHPRESS_ALLOWED_IPS="WORDPRESS_SERVER_IP,YOUR_ADMIN_IP"
The app-layer allowlist only accepts exact IP addresses. It is a backstop, not a replacement for firewall rules.
Diagnostics
Use MeshPress > Diagnostics & Manual Actions first. It calls these bridge endpoints:
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/health
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/version
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/status
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/self-test
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/groups?refresh=1
curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/devices?refresh=1
/api/self-test verifies the bridge can find meshctrl, has an auth method, has a public MeshCentral URL, and can list device groups and devices.
For direct session links, test both URL styles:
curl -H "Authorization: Bearer TOKEN" "http://127.0.0.1:3099/api/session-links/NODEID"
curl -H "Authorization: Bearer TOKEN" "http://127.0.0.1:3099/api/link/console/NODEID?link_mode=all"
To trigger a catalog-managed bridge update from MeshPress admin or curl:
curl -X POST -H "Authorization: Bearer TOKEN" http://127.0.0.1:3099/api/update-now
To test the first provisioning path, enable commands and create a temporary group:
curl -X POST \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"MeshPress Test Group","description":"Manual bridge validation"}' \
http://127.0.0.1:3099/api/groups/create
MeshCentral Auth
The bridge supports either a MeshCentral login key file or a dedicated integration username/password.
For username/password auth, set:
MESHCTRL_USER="your-generated-integration-user"
MESHCTRL_PASS="your-generated-integration-password"
If MESHCTRL_LOGIN_KEY_FILE exists, the bridge will prefer it. If the file does not exist and MESHCTRL_PASS is set, it will use username/password auth.
MeshCentral Login Key Option
On the MeshCentral server:
cd /opt/meshcentral
sudo node node_modules/meshcentral --loginTokenKey > /etc/gracepress/meshcentral-login.key
sudo chmod 600 /etc/gracepress/meshcentral-login.key
MeshCentral must have login tokens enabled in meshcentral-data/config.json:
{
"settings": {
"allowLoginToken": true
}
}
Restart MeshCentral after changing config.