---
title: "SSH Keys — API v1 and MCP | Nodea Documentation"
description: "Complete SSH Keys module reference: REST API v1 endpoints (list, add an OpenSSH public key with fingerprint, rename, delete), infra:read/write/delete scopes, MCP tools and curl / JSON-RPC examples."
url: "https://nodea.io/en/api-docs/ssh-keys"
lang: "en"
updated: "2026-11-13"
---

# SSH Keys — API v1 and MCP | Nodea Documentation

> SSH Keys module reference — API v1 endpoints (list, add a public key, rename, delete), infra:read/write/delete scopes, SHA256 fingerprint, MCP tools and examples.

The **SSH Keys** module manages the user's public SSH keys — the ones deployed to servers during provisioning. It covers listing, adding a key, renaming it and deleting it. This document covers the REST API v1 surface (/api/v1/ssh-keys) 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 keys.
- infra:write — add a key and rename it.
- infra:delete — delete a key.

The whole module is additionally gated by the Pennant flag features:servers — disabling the servers module hides SSH keys on both web UI and API/MCP. Rate limit: throttle:api-v1 = 240/min per token user.

## Ownership and security

Row-level access is enforced by UserSshKeyPolicy (owner-only for update/delete). Listing is owner-scoped by construction. A foreign id on edit/delete yields 403. There is no sharing model for SSH keys.

**Important:** the content column holds only the **public** key (encrypted at rest), so returning it is safe — private keys never enter this table. Never submit a private key. Key material is immutable: editing only changes the display name (replacing a key = delete + create).

## API v1 — endpoints

MethodPathScopeDescription GET/api/v1/ssh-keysinfra:readThe user's keys, paginated (newest first). POST/api/v1/ssh-keysinfra:writeAdd an OpenSSH public key. Returns 201. The key is also sent, through the nodeaD agent, to every connected server you manage with root-task rights — it cannot be limited to one server. PUT/PATCH/api/v1/ssh-keys/{sshKey}infra:writeRename a key's display name (owner-only). DELETE/api/v1/ssh-keys/{sshKey}infra:deleteDelete a key (owner-only). Returns 204. The key is also removed from the connected servers it was distributed to.

### GET /ssh-keys — parameters and response

- per\_page (int 1–100, default 20), page (int ≥1).

Response: a UserSshKeyResource collection — fields: id, name, for\_root (bool), public\_key (the key content), fingerprint (SHA256, ssh-keygen -lf style — SHA256:... without padding; null when the content is not parseable as \<type\> \<base64\> \[comment\]), created\_at, updated\_at.

### POST — body parameters

- name (string, required, min 3, max 255) — display name.
- content (string, required, min 3, max 10000, ValidPublicSshKey rule) — public key material in OpenSSH format. The rule round-trips the key through ssh-keygen -l, so invalid material yields 422.
- for\_root (bool, optional) — whether the key should be installed for the root user during provisioning.

### PATCH — body parameters

- name (string, required, min 3, max 255) — the only mutable field.

### Error codes

- 401 — missing/invalid token.
- 403 — missing required scope or a foreign key (owner-only policy).
- 404 — unknown id.
- 422 — validation (invalid public key, name too short/long).

## Examples — API

### List keys

curl -s https://app.nodea.io/api/v1/ssh-keys \\ -H "Authorization: Bearer $NODEA\_TOKEN" \\ -H "Accept: application/json"

### Add a public key (for root)

curl -s -X POST https://app.nodea.io/api/v1/ssh-keys \\ -H "Authorization: Bearer $NODEA\_TOKEN" \\ -H "Content-Type: application/json" \\ -d '{ "name": "laptop-ed25519", "content": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... user@host", "for\_root": true }'

### Rename a key

curl -s -X PATCH https://app.nodea.io/api/v1/ssh-keys/17 \\ -H "Authorization: Bearer $NODEA\_TOKEN" \\ -H "Content-Type: application/json" \\ -d '{ "name": "work-laptop" }'

## MCP — tools

The MCP tools share the authentication and scope model with API v1 (Bearer + scope checked in ensureScope) and the features:servers flag (a tool on an inactive flag disappears from tools/list). Keys are addressed by integer id; a missing or foreign id raises the same error (no cross-tenant enumeration). The output uses the same shape as the API (UserSshKeyResource).

ToolScopeModeDescription ssh-keys-listinfra:readread-onlyList keys; page, per\_page (max 50). Returns id/name/for\_root/public\_key/fingerprint/dates. ssh-key-createinfra:writewriteAdd an OpenSSH public key; name (min 3), content, for\_root. Validated via ssh-keygen. Never pass a private key. ssh-key-deleteinfra:deletedestructiveDelete a key by id; requires confirm: true. The key is also removed, through the nodeaD agent, from the connected servers it was distributed to.

### Example — MCP (JSON-RPC)

{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "ssh-key-create", "arguments": { "name": "laptop-ed25519", "content": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... user@host", "for\_root": true } } } { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "ssh-key-delete", "arguments": { "id": 17, "confirm": true } } }

## Related

- [Servers — API v1 and MCP | Nodea Documentation](https://nodea.io/en/api-docs/servers)
- [DNS](https://nodea.io/en/glossary/dns)
- [Devices (Connection Manager) — API v1 and MCP | Nodea Documentation](https://nodea.io/en/api-docs/vault-devices)
- [DNS — API v1 and MCP | Nodea Documentation](https://nodea.io/en/api-docs/dns)

---

Languages: [pl](https://nodea.pl/pl/dokumentacja-api/ssh-keys.md)  
Site index: [llms.txt](https://nodea.io/en/llms.txt)
