Nodea — logo

Devices (Connection Manager) — API v1 and MCP | Nodea Documentation

The Devices module manages paired instances of the nodea-cm connection manager — a desktop app acting as a zero-knowledge, end-to-end-encrypted connection store. The API lets you list devices, rename them and revoke them. This document covers the REST API v1 surface (/api/v1/vault-devices) and the MCP tools in this module.

Authentication and scopes

API v1 authenticates with a Passport personal access token via the Authorization: Bearer <token> header. Access tiers:

  • infra:read — list devices.
  • infra:write — rename a device.
  • infra:delete — revoke a device.

The module is ungated — there is no Pennant flag (like web /app/connection-manager). Rate limit: throttle:api-v1 = 240/min per token user.

Scope of this API vs. the desktop

This API handles device management only (list / rename / revoke). All enrollment and cryptography — vault sync, account keys, VMK, recovery blob — belongs to the desktop client and is exposed separately under /api/vault/* (the vault guard, vault scope). API v1 never modifies a device's cryptographic state.

Ownership, addressing and security

The VaultDevice model has no policy class — every query is scoped to user_id = token user. Devices are addressed by their opaque device_id (UUID), never the internal primary key. A foreign, unknown or already-revoked (soft-deleted) device yields 404 (firstOrFail; the default SoftDeletes scope hides revoked rows).

Security: only opaque metadata is serialized. The crypto blobs — wrapped_vmk and device_public_key — are NEVER returned by any response; enrollment state surfaces solely as the has_wrapped_vmk boolean.

Revoke (DELETE) mirrors the web exactly: VaultDevice::revokeAccess() soft-deletes the row AND invalidates the Passport access token the device last authenticated with (plus its refresh tokens). This ensures a stolen or unpaired device cannot keep calling /api/vault/* on a still-unexpired bearer token. The operation is irreversible — the device must be paired again from scratch.

API v1 — endpoints

MethodPathScopeDescription
GET/api/v1/vault-devicesinfra:readThe user's paired devices (unpaginated; the set is bounded by physical pairings, newest first).
PATCH/api/v1/vault-devices/{device}infra:writeRename a device. Cosmetic — id, keys and access unchanged.
DELETE/api/v1/vault-devices/{device}infra:deleteRevoke a device (soft-delete + Passport token invalidation). Returns 204.

Response — VaultDeviceResource

Fields: device_id (UUID), name, has_wrapped_vmk (bool — whether the device completed enrollment), last_seen (ISO8601, nullable), created_at (ISO8601). No cryptographic fields whatsoever.

PATCH — body parameters

  • name (string, required, max 255) — new display name (the only mutable field).

Error codes

  • 401 — missing/invalid token.
  • 403 — missing required scope.
  • 404 — unknown, foreign or already-revoked device.
  • 422 — validation (empty/too-long name).

Examples — API

List devices

curl -s https://app.nodea.io/api/v1/vault-devices \
  -H "Authorization: Bearer $NODEA_TOKEN" \
  -H "Accept: application/json"

Rename a device

curl -s -X PATCH https://app.nodea.io/api/v1/vault-devices/9b1f2c3d-... \
  -H "Authorization: Bearer $NODEA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "MacBook Pro (work)" }'

Revoke a device

curl -s -X DELETE https://app.nodea.io/api/v1/vault-devices/9b1f2c3d-... \
  -H "Authorization: Bearer $NODEA_TOKEN" \
  -H "Accept: application/json"

MCP — tools

The MCP tools share the authentication and scope model with API v1 (Bearer + scope checked in ensureScope). The module is ungated (no feature flag). Devices are addressed by device_id (UUID); a missing, foreign or already-revoked id raises the same error (no cross-tenant enumeration). Responses carry metadata only — no crypto blobs.

ToolScopeModeDescription
vault-devices-listinfra:readread-onlyList paired devices; page, per_page (max 50). Returns device_id/name/has_wrapped_vmk/last_seen/created_at. Revoked devices are not listed.
vault-device-renameinfra:writeidempotentRename; device_id, name (max 255). Cosmetic — id, keys and access unchanged.
vault-device-revokeinfra:deletedestructiveRevoke; device_id + required confirm: true. The device's API tokens are invalidated immediately; irreversible (re-pairing required).

Example — MCP (JSON-RPC)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "vault-device-rename",
    "arguments": { "device_id": "9b1f2c3d-...uuid", "name": "MacBook Pro (work)" }
  }
}
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "vault-device-revoke",
    "arguments": { "device_id": "9b1f2c3d-...uuid", "confirm": true }
  }
}