Skip to main content
Version: 10

Authenticate and Encrypt Runtime Endpoints

For a normal desktop connection, you only need to:

  1. Start the Runtime. The MoveIt Pro CLI creates and stores its frontend key automatically.
  2. Select the Runtime in the desktop app.
  3. If the app asks for the key, run moveit_pro frontend-key on 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.

Identity metadata is public to reachable clients

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.

Direct Docker Compose deployments

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

Required Version
This feature will not be released until MoveIt Pro version 10.0.

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

Required Version
This feature will not be released until MoveIt Pro version 10.0.

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 for POST /compatibility and GET /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.com
    curl --cacert /path/to/ca.pem \
    -H "Authorization: Bearer example-not-a-real-credential" \
    "https://${backend_host}:3200/objectives"
  • MCP Streamable HTTP — connect to /mcp on the deployment's REST port and send the same bearer header:

    https://<certificate-hostname>:3200/mcp
    Authorization: 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_ORIGINS is needed only for browser-based clients that send Origin. Native clients normally omit that header.

  • WebSocket bridge (foxglove_bridge, port 3201) — direct clients speaking the Foxglove WebSocket protocol send Authorization: Bearer <key> with the WebSocket upgrade request. The desktop app handles this automatically.

  • Video (WHEP signaling, port 3202) — send Authorization: Bearer <key> on the POST /video/ros/<topic>/whep request. 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 Authorization header. Do not place the key in a URL.
  • WebRTC media on port 3203 for 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 on 3202 returns. 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 6d6f766569742d70726f2d6c6f63616c2d646576656c6f706d656e742d6b6579 value 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.