Authenticate and Encrypt Runtime Endpoints
For a normal desktop connection, you only need to:
- Start the Runtime. The MoveIt Pro CLI creates and stores its frontend key automatically.
- Select the Runtime in the desktop app.
- If the app asks for the key, run
moveit_pro frontend-keyon the Runtime computer and enter the displayed value.
The desktop app checks the Runtime certificate for you. The remaining sections explain the security model and the additional setup needed by direct API or SDK clients.
MoveIt Pro requires a shared frontend key on authenticated client-facing
endpoints of the MoveIt Pro Runtime:
the authenticated REST API operations, MCP Streamable HTTP, the WebSocket bridge
(foxglove_bridge), the video stream, and the terminal. Starting in MoveIt Pro 10.0, a small bootstrap and
observability surface remains unauthenticated: signed REST GET /identity, GET /health, the interactive API
documentation and schema at /docs, /redoc, and /openapi.json, and HTTP
OPTIONS preflight requests. Direct API clients and operator pairing tools can call
/identity without a bearer key to verify the deployment before choosing or
transmitting credentials. Separately, the desktop app uses the discovery service
on port 3204 to authenticate the Runtime's TLS certificate with the frontend key
before sending that key to a Runtime endpoint. Same-account local adoption uses an
owner-only local socket. The Runtime endpoints are served over TLS.
GET /identity requires no bearer key and is CORS-readable by any web origin
that can reach the REST port. Its signed response includes the stable deployment
UUID, which persists across restarts and can link observations of the same
deployment; a per-process incarnation UUID; the public JWK and its fingerprint;
issuance and expiration timestamps; the caller's echoed nonce; and the signature.
Expose the REST port only within a trusted network boundary, or through the
trusted TLS ingress or SSH tunnel used for pairing. This endpoint does not send
telemetry to PickNik or alter PickNik's data-collection policy: requests and
response data remain within infrastructure controlled by the operator.
Frontend Key
The frontend key protects access to the Runtime. The MoveIt Pro CLI creates and stores a unique key automatically. The desktop app also uses it to authenticate the Runtime's TLS certificate before sending credentials.
To display the active key, run this command on the Runtime computer:
moveit_pro frontend-key
Treat the key like a password, and share it only with people and tools that need to reach the deployment. The same key protects every authenticated Runtime endpoint.
To replace the stored key, run:
moveit_pro configure --rotate-frontend-key
Restart the Runtime afterward and update saved desktop connections and other clients with the new key.
Deployments started without the MoveIt Pro CLI must provide
MOVEIT_FRONTEND_KEY in their environment or .env file. Generate one with
openssl rand -hex 32.
Inference server
The optional model inference server (moveit_pro run --with-inference-server
or --only-inference-server) runs from your own workspace, so this is the
contract that server must implement: MoveIt Pro passes MOVEIT_FRONTEND_KEY
into the container, and the server is responsible for requiring it as an
Authorization: Bearer token on /infer and refusing inference without it.
Leave /health token-free for health probes, matching the REST API's
/health. Unlike the other Runtime endpoints, the inference server serves
plain HTTP without TLS; both of its endpoints publish on 127.0.0.1 only, and
that loopback confinement is what keeps them unreachable from other machines.
TLS certificates
Runtime backend TLS is required. MoveIt Pro uses MOVEIT_TLS_CERT_FILE and
MOVEIT_TLS_KEY_FILE when both are set; setting only one is an error. Without
an override pair, the Runtime generates a self-signed certificate once under
the persistent MoveIt Pro data directory and reuses it on later starts. Set
MOVEIT_TLS_CERT_DIR to relocate that generated pair.
export MOVEIT_TLS_CERT_FILE=/path/to/fullchain.pem
export MOVEIT_TLS_KEY_FILE=/path/to/privkey.pem
moveit_pro run
Self-signed certificates are enough to encrypt client-to-Runtime traffic on a trusted network. The desktop app authenticates the current Runtime certificate automatically; external clients may need to trust it explicitly.
For each connection, the desktop app asks the discovery service for the Runtime's current certificate and checks it with the saved frontend key. Only then does it send the key to the Runtime. This happens automatically, including after the Runtime certificate changes. If the check fails, the app stops the connection before sharing the key.
Direct remote SDK clients should use a certificate signed by a CA their host
already trusts and whose subject alternative names cover the hostname or IP
address in the client URL. The generated certificate covers loopback names
only. For a local or tunneled development connection, copy cert.pem from the
persistent TLS directory to the client and add it to that client host's trust
store before connecting through loopback. Follow the operating system or client
runtime's certificate-trust instructions. Do not disable certificate
verification.
Using the desktop app
The desktop app stores each profile's frontend key with the operating system credential store. It never returns the stored value to the renderer and has no unverified-certificate mode. See Connect with the MoveIt Pro Desktop App.
Using the endpoints directly
Before a direct or operator-managed client sends a frontend key, verify the standalone Runtime identity over trusted TLS or an SSH tunnel. Replace both demonstration pin values below with the deployment ID and fingerprint recorded during pairing:
export SSL_CERT_FILE='/path/to/ca.pem'
identity_url='https://moveit-pro.example.com:3200/identity'
python3 -m moveit_studio_rest_api.verify_backend_identity \
--url "${identity_url}" \
--expected-deployment-id '00000000-0000-4000-8000-000000000000' \
--expected-fingerprint 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
The verifier generates a nonce from 128 bits of CSPRNG entropy and sends no
Authorization header. It validates the nonce and validity window, verifies the
Ed25519 signature, recomputes the RFC 7638 kid, and matches it to the previously
paired deployment ID and fingerprint. It rejects redirects and exits unsuccessfully
on any mismatch. Omit both --expected-deployment-id and
--expected-fingerprint only during an explicit first pairing over authenticated
TLS, then record both printed values through the approved pairing channel.
Only after verification succeeds should the client send credentials. A changed or
unknown identity requires explicit re-pairing; never try stored credentials against
it automatically.
Bootstrap rate limiting
The unauthenticated identity endpoint permits 30 valid nonce-bearing signing
requests per distinguishable transport source in each 60-second window. On quota
exhaustion it returns HTTP
429; clients must wait for the Retry-After value, which matches
retryAfterSeconds in the response body. The source map is bounded and old entries
are evicted so connection churn cannot exhaust Runtime memory.
The shipped REST listener ignores forwarding headers (proxy_headers=False). Thus,
requests arriving through a reverse proxy share that proxy's quota, while direct
non-loopback TLS peers are isolated by their transport source address. Do not enable
forwarding-header trust without a dedicated ingress boundary
that local processes cannot impersonate. There is no process-wide identity request
cap because unauthenticated traffic could use it to deny service to unrelated
sources. Deployments that expose the Runtime beyond a trusted network need
network-layer connection and denial-of-service controls at their trusted ingress.
Runtime identity backup, rotation, and recovery
The deployment identity and signing key under the persistent data root are a single
trust-state pair. Back up and restore the complete backend_identity directory
together; never copy or manually repair only one file. A partial, malformed, or
permission-unsafe pair intentionally prevents Runtime startup. To rotate identity,
stop every Runtime process, back up the complete directory, move the old directory
aside, then start one Runtime instance and explicitly re-pair clients over trusted TLS or an
SSH tunnel. If corruption prevents startup, restore both files from the same
known-good backup; otherwise rotate and re-pair. Preserve 0700 directory and
0600 file permissions. Identity rotation does not itself rotate
MOVEIT_FRONTEND_KEY or TLS certificate trust; perform those operations separately
when required.
The authenticated operations described below must present the frontend key. The bootstrap, health, API documentation and schema, and preflight exemptions listed above do not:
-
REST API — send an
Authorization: Bearer ***header, including forPOST /compatibilityandGET /frontend-settings. REST query-string credentials are rejected so the credential cannot leak through access logs. This command uses a deliberately invalid demonstration value:backend_host=moveit-pro.example.comcurl --cacert /path/to/ca.pem \-H "Authorization: Bearer example-not-a-real-credential" \"https://${backend_host}:3200/objectives" -
MCP Streamable HTTP — connect to
/mcpon the deployment's REST port and send the same bearer header:https://<certificate-hostname>:3200/mcpAuthorization: Bearer <key>MCP additionally validates the HTTP Host and Origin headers to prevent DNS rebinding. Loopback values are allowed by default. For remote access, set comma-separated allowlists before starting MoveIt Pro:
export MOVEIT_MCP_ALLOWED_HOSTS="robot.example.com:3200"export MOVEIT_MCP_ALLOWED_ORIGINS="https://agent.example.com"MOVEIT_MCP_ALLOWED_ORIGINSis needed only for browser-based clients that send Origin. Native clients normally omit that header. -
WebSocket bridge (
foxglove_bridge, port3201) — direct clients speaking the Foxglove WebSocket protocol sendAuthorization: Bearer <key>with the WebSocket upgrade request. The desktop app handles this automatically. -
Video (WHEP signaling, port
3202) — sendAuthorization: Bearer <key>on thePOST /video/ros/<topic>/wheprequest. The desktop app handles this automatically.
Security notes
- The frontend key is a bearer secret: anyone who has it can reach the endpoints.
- The frontend key authenticates a deployment, not an individual user. It does not provide per-user identity or scopes.
- Direct clients should send the frontend key in the
Authorizationheader. Do not place the key in a URL. - WebRTC media on port
3203for the default deployment is client-facing but is not gated by the frontend key: it carries only DTLS/SRTP media whose keys come from the SDP answer that the authenticated WHEP handshake on3202returns. Media stays confidential, but because the port cannot authenticate, it accepts TCP connections from anyone who can reach it and holds them open. Restrict it to the client hosts that need it rather than opening it to the whole network. Named instances use their assigned or discovered WebRTC media port. - The documented
6d6f766569742d70726f2d6c6f63616c2d646576656c6f706d656e742d6b6579value is only for local development. Set a deployment-specific key for shared, remote, or production deployments. - Runtime TLS protects desktop-app and direct-client traffic.
- The Runtime serves TLS unconditionally. Keep certificates and private keys in a protected location and rotate the frontend key if it is exposed.