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
| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /api/v1/vault-devices | infra:read | The user's paired devices (unpaginated; the set is bounded by physical pairings, newest first). |
| PATCH | /api/v1/vault-devices/{device} | infra:write | Rename a device. Cosmetic — id, keys and access unchanged. |
| DELETE | /api/v1/vault-devices/{device} | infra:delete | Revoke 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.
| Tool | Scope | Mode | Description |
|---|---|---|---|
| vault-devices-list | infra:read | read-only | List 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-rename | infra:write | idempotent | Rename; device_id, name (max 255). Cosmetic — id, keys and access unchanged. |
| vault-device-revoke | infra:delete | destructive | Revoke; 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 }
}
}