Documentation
Everything you need to deploy and operate Lockwave.
Quick Start
Get running in three steps:
- [Create your account]({{ route('register') }}) and set up your first team
- Generate an SSH key or import your existing public key
- Enroll your first host using the install command below
curl -fsSL https://get.lockwave.io/install.sh | sudo bash -s -- \
--token YOUR_ENROLLMENT_TOKEN \
--api-url https://lockwave.io \
--os-user deploy
The enrollment token is generated in the dashboard when you enroll a new host. It expires after 15 minutes.
Architecture
Lockwave consists of two components:
- Control Plane — The Lockwave web application that stores teams, SSH public keys, hosts, and assignments. It computes the desired state for each host and serves it via a REST API.
- Daemon (lockwaved) — A statically compiled Go binary that runs on each managed host. It polls the control plane over outbound HTTPS, computes the delta between current and desired state, and atomically rewrites the authorized_keys file.
The daemon never opens inbound ports. All communication flows from the daemon to the control plane, never the reverse. This means you don't need to modify firewall rules or open SSH tunnels.
Daemon Installation
The install script downloads the correct binary for your architecture, creates a systemd service, and registers the host with the control plane.
Supported Platforms
- Linux (amd64, arm64)
- macOS / Darwin (amd64, arm64)
- FreeBSD (amd64)
Verifying Installation
systemctl status lockwaved
journalctl -u lockwaved -f
The host should appear in your dashboard with a "healthy" status within 60 seconds.
Uninstalling
To remove the daemon from a host, run the install script with --uninstall. It stops the service, removes the binary, systemd unit, sshd drop-in config, and the config directory.
curl -fsSL https://get.lockwave.io/install.sh | sudo bash -s -- --uninstall
Daemon behavior
The daemon can apply optional security hardening and key modes per host:
- Block password authentication — When enabled for a host in the dashboard, the daemon writes a drop-in file under
/etc/ssh/sshd_config.d/(e.g.99-lockwave.conf) to setPasswordAuthentication noandKbdInteractiveAuthentication no. It runssshd -tto validate, then reloads sshd. The file is overwritten on the next sync. - Exclusive keys — When the server sends
exclusive_keys: truefor a managed OS user, the daemon replaces the entireauthorized_keysfile with only the Lockwave-managed keys (no keys outside the block are preserved). Configure per host or per OS user in the dashboard. - IP binding — The server records the IP address used when the daemon first registers. This IP is never automatically updated. On each sync, the server compares the daemon's request IP to the registered IP. On mismatch, an audit event (
host.ip_mismatch) is logged and the dashboard shows a warning. The registered IP only changes when you manually accept the new IP from the host detail page.
For full daemon documentation including configuration, self-update, and troubleshooting, see the Lockwave daemon README on GitHub.
Key Management
Lockwave supports two key types:
- ed25519 — Default and recommended. Smaller, faster, and more secure than RSA.
- RSA 4096 — For legacy compatibility with older SSH clients or servers.
Keys can be generated server-side (the private key is shown once and never stored) or imported by pasting your existing public key. Each key has a source (e.g. generated or imported) and optional tags for filtering and reporting.
Keys are scoped to teams. Visibility options:
- Personal — Only visible to the key owner
- Shared — Visible to team admins and owners
Assignments
Assignments map SSH keys to hosts and OS users. They define the desired state of each authorized_keys file.
Create an assignment to grant access. Delete it to revoke. Changes propagate to all affected hosts on the next daemon sync cycle. Assignments can include an optional description and record who created them for audit.
Sync intervals depend on your plan: Free (5 min), Standard (60 sec), Business (30 sec), Enterprise (15 sec).
API Reference
The Lockwave REST API lets you manage all resources programmatically. All endpoints are versioned under /api/v1/.
Authentication
Create an API token from your profile settings. Include it as a Bearer token in every request:
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json" \
https://lockwave.io/api/v1/ssh-keys
Team Context
All endpoints operate in the context of a team. By default, your current team is used. To target a different team, pass the X-Team-Id header:
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
-H "X-Team-Id: YOUR_TEAM_UUID" \
-H "Accept: application/json" \
https://lockwave.io/api/v1/hosts
Base URL
https://lockwave.io/api/v1/
Endpoints
User & Teams
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/user |
Authenticated user profile |
GET |
/api/v1/teams |
List your teams |
GET |
/api/v1/teams/{team} |
Show team details |
SSH Keys
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/ssh-keys |
List team keys |
POST |
/api/v1/ssh-keys |
Import or generate key |
GET |
/api/v1/ssh-keys/{id} |
Show key details |
PATCH |
/api/v1/ssh-keys/{id} |
Update key name/comment |
DELETE |
/api/v1/ssh-keys/{id} |
Soft-delete key |
POST |
/api/v1/ssh-keys/{id}/block |
Block key |
POST |
/api/v1/ssh-keys/{id}/unblock |
Unblock key |
Hosts
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/hosts |
List team hosts |
POST |
/api/v1/hosts |
Create host |
GET |
/api/v1/hosts/{id} |
Show host detail |
PATCH |
/api/v1/hosts/{id} |
Update host (including security settings: block_password_auth, exclusive_keys_default, enforce_ip_binding, tags) |
DELETE |
/api/v1/hosts/{id} |
Delete host |
Host Users
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/hosts/{id}/users |
List OS users |
POST |
/api/v1/hosts/{id}/users |
Create OS user |
PATCH |
/api/v1/hosts/{id}/users/{uid} |
Update OS user |
DELETE |
/api/v1/hosts/{id}/users/{uid} |
Delete OS user |
Enrollment & Credentials
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/hosts/{id}/enrollment-tokens |
Generate enrollment token |
POST |
/api/v1/hosts/{id}/credentials/rotate |
Rotate daemon credentials |
Assignments
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/assignments |
List team assignments |
POST |
/api/v1/assignments |
Create assignment |
GET |
/api/v1/assignments/{id} |
Show assignment |
DELETE |
/api/v1/assignments/{id} |
Delete assignment |
Break Glass
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/break-glass |
List events |
POST |
/api/v1/break-glass/activate |
Activate break glass |
POST |
/api/v1/break-glass/{id}/deactivate |
Deactivate break glass |
Audit Events
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/audit-events |
List paginated audit log |
GET |
/api/v1/audit-events/{id} |
Show single event |
Reports
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/reports |
List reports |
POST |
/api/v1/reports |
Request new report |
GET |
/api/v1/reports/{id}/download |
Download report file |
Webhook Endpoints
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/webhook-endpoints |
List webhook endpoints |
POST |
/api/v1/webhook-endpoints |
Create webhook endpoint |
GET |
/api/v1/webhook-endpoints/{id} |
Show webhook endpoint |
PUT |
/api/v1/webhook-endpoints/{id} |
Update webhook endpoint |
DELETE |
/api/v1/webhook-endpoints/{id} |
Delete webhook endpoint |
GET |
/api/v1/webhook-endpoints/{id}/deliveries |
List delivery attempts |
POST |
/api/v1/webhook-endpoints/{id}/test |
Send test webhook |
Notification Channels
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/notification-channels |
List notification channels |
POST |
/api/v1/notification-channels |
Create notification channel |
GET |
/api/v1/notification-channels/{id} |
Show notification channel |
PUT |
/api/v1/notification-channels/{id} |
Update notification channel |
DELETE |
/api/v1/notification-channels/{id} |
Delete notification channel |
POST |
/api/v1/notification-channels/{id}/test |
Send test notification |
Audit Log Stream
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/audit-stream |
Show stream configuration |
PUT |
/api/v1/audit-stream |
Create or update stream |
DELETE |
/api/v1/audit-stream |
Delete stream |
POST |
/api/v1/audit-stream/test |
Send test event to stream |
Request Examples
List SSH Keys
curl -s -H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" \
https://lockwave.io/api/v1/ssh-keys
Create a Host
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"display_name":"web-01","hostname":"web-01.example.com","os":"linux","arch":"amd64"}' \
https://lockwave.io/api/v1/hosts
Create an Assignment
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"host_user_id":"HOST_USER_UUID","ssh_key_id":"SSH_KEY_UUID"}' \
https://lockwave.io/api/v1/assignments
Cursor Pagination
All list endpoints use cursor-based pagination. Results include next_cursor and prev_cursor in the meta object. Pass ?cursor=VALUE to navigate pages. Default page size is 25 items.
Error Handling
All errors return a consistent JSON format:
{
"message": "The given data was invalid.",
"errors": {
"name": ["The name field is required."]
}
}
| Status | Meaning |
|---|---|
400 |
Bad request / malformed JSON |
401 |
Missing or invalid token |
403 |
Insufficient permissions |
404 |
Resource not found |
422 |
Validation error |
429 |
Rate limit exceeded |
Rate Limiting
The API allows 60 requests per minute per authenticated user. Rate limit status is returned in response headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
When the limit is exceeded, a 429 Too Many Requests response is returned with a Retry-After header indicating seconds to wait.
Webhooks
Lockwave can send HTTP POST callbacks to your endpoints whenever events occur in your team. Use webhooks to integrate with your internal tooling, ChatOps pipelines, or SIEM systems.
Creating a Webhook Endpoint
Navigate to your team settings and click Webhooks. Add a new endpoint with a publicly accessible HTTPS URL. Lockwave will send a POST request with a JSON payload for each event.
Verifying Signatures
Every webhook request includes an X-Lockwave-Signature header containing an HMAC-SHA256 hash of the request body, signed with your webhook secret. Always verify this signature before processing the payload.
$signature = hash_hmac('sha256', $requestBody, $webhookSecret);
if (! hash_equals($signature, $headerSignature)) {
abort(401);
}
Event Types
Webhooks are dispatched for the following event types:
key.created,key.blocked,key.unblocked,key.deletedhost.enrolled,host.deleted,host.drift_detectedassignment.created,assignment.deletedbreak_glass.activated,break_glass.deactivatedteam.member_added,team.member_removed
Delivery & Retries
Lockwave expects a 2xx response within 10 seconds. Failed deliveries are retried up to 5 times with exponential backoff (1 min, 5 min, 30 min, 2 hr, 24 hr). All delivery attempts are logged and visible in the webhook delivery log.
Plan Limits
Webhooks are available on all plans with different endpoint limits: Free (3), Standard (10), Business (25), Enterprise (unlimited).
Terraform Provider
The official Lockwave Terraform provider lets you manage SSH infrastructure as code. Define hosts, keys, assignments, webhook endpoints, notification channels, and audit log streams in HCL and apply them through your existing IaC pipeline.
Installation
terraform {
required_providers {
lockwave = {
source = "lockwave-io/lockwave"
version = "~> 0.1"
}
}
}
provider "lockwave" {
api_token = var.lockwave_api_token # required, sensitive
team_id = var.lockwave_team_id # required
# api_url = "https://lockwave.io" # optional — defaults to https://lockwave.io
}
All three arguments can also be set via environment variables: LOCKWAVE_API_TOKEN, LOCKWAVE_TEAM_ID, LOCKWAVE_API_URL.
Example Usage
# Generate an ed25519 key — private key is returned once on creation
resource "lockwave_ssh_key" "deploy" {
name = "deploy-key"
mode = "generate"
key_type = "ed25519"
}
# Create a host
resource "lockwave_host" "web01" {
display_name = "web-01"
hostname = "web-01.example.com"
os = "linux"
arch = "x86_64"
}
# Add an OS user to the host
resource "lockwave_host_user" "ubuntu" {
host_id = lockwave_host.web01.id
os_user = "ubuntu"
}
# Assign the key to the host user
resource "lockwave_assignment" "deploy_to_web01" {
ssh_key_id = lockwave_ssh_key.deploy.id
host_user_id = lockwave_host_user.ubuntu.id
}
# Subscribe to events via webhook
resource "lockwave_webhook_endpoint" "ops" {
url = "https://hooks.example.com/lockwave"
description = "Operations alerts"
events = ["host.synced", "ssh_key.created", "assignment.created"]
}
# Alert on Slack
resource "lockwave_notification_channel" "slack" {
type = "slack"
name = "Engineering alerts"
config {
webhook_url = var.slack_webhook_url
}
}
# Stream audit logs to SIEM
resource "lockwave_audit_log_stream" "siem" {
type = "webhook"
config {
url = "https://siem.example.com/lockwave"
}
}
Resources
lockwave_host— Manage hosts (provisions a one-time daemon credential on creation)lockwave_host_user— Manage OS user records on hosts (authorized_keys targets)lockwave_ssh_key— Generate or import SSH keyslockwave_assignment— Map keys to host users (grant access)lockwave_webhook_endpoint— Manage webhook endpoints for event notificationslockwave_notification_channel— Manage notification channels (Slack, email)lockwave_audit_log_stream— Stream audit events to webhook or S3
Data Sources
lockwave_team— Fetch the current teamlockwave_host/lockwave_hosts— Read host detailslockwave_ssh_key/lockwave_ssh_keys— Read SSH key details
All resources support terraform import. The provider wraps the Lockwave REST API v1. Full documentation is available on the Terraform Registry and OpenTofu Registry.
Integrations
Lockwave integrates with the tools your team already uses for communication, automation, and compliance.
Notification Channels
Connect notification channels to receive real-time alerts for security events. Available on Standard plans and above.
- Slack — Send alerts to a Slack channel via incoming webhook
- Discord — Post notifications to a Discord channel via webhook
- Microsoft Teams — Deliver alerts to a Teams channel via connector
- Email — Send notification emails to a distribution list or individual address
Each channel supports granular event filtering so you only receive alerts for the events that matter to your team.
Audit Log Streaming
Stream your audit log to external destinations for long-term retention and SIEM integration. Available on Business plans and above.
- Webhook delivery — Forward audit events in real time to an HTTPS endpoint
- S3-compatible storage — Batch and deliver audit log files to an S3 bucket (AWS S3, MinIO, Cloudflare R2)
Dashboard
The Lockwave dashboard displays host sync status, daemon heartbeats, drift events, and assignment changes. Refresh the page to see the latest state from your daemons.
MCP Server
Lockwave exposes a Model Context Protocol (MCP) server so AI assistants and agents can interact with your team's data—hosts, SSH keys, assignments, break-glass, reports, and audit—using tools, resources, and prompts.
Endpoint
POST https://lockwave.io/mcp/lockwave
All MCP JSON-RPC requests (initialize, tools/list, tools/call, resources/read, prompts/get, etc.) are sent as POST to this URL.
Authentication
Use the same API token as the REST API. Send it as a Bearer token in the Authorization header:
Authorization: Bearer YOUR_API_TOKEN
Your user must have a current team set (the same team context as the REST API). All tools and resources are scoped to that team.
Capabilities
The Lockwave MCP server provides:
- Tools — List and get hosts, SSH keys, assignments; create or delete assignments; list/activate/deactivate break-glass events; list or request compliance reports; list audit events.
- Resources — Static docs (overview, team summary) and URI-templated resources for host and key details (
lockwave://hosts/{id},lockwave://keys/{id}). - Prompts — Reusable prompts to summarize the team or guide adding a key to a host.
Supported MCP clients
Add the Lockwave MCP server in any of these clients using the endpoint and Bearer token above.
Client-specific setup
- Open Settings (
Cmd + ,orCtrl + ,) and go to Tools & MCP. - Click Add new MCP server.
- Set Type to
streamableHttp, URL to the endpoint above, and add headerAuthorization: Bearer YOUR_API_TOKEN. - Restart Cursor for changes to take effect.
- In Claude Desktop, go to Settings → Developer → Edit Config.
- Add an MCP server entry with the Lockwave endpoint URL and configure your API token (e.g. in
headersorenvdepending on your client version). - Restart Claude Desktop after saving the config.
Claude Code (VS Code / IDE) can use the same config file as Claude Desktop, or you can add the Lockwave server via the CLI:
- Open a terminal and run (
--scope usersaves to your user config):
claude mcp add --transport http --header "Authorization: Bearer YOUR_API_TOKEN" --scope user lockwave https://lockwave.io/mcp/lockwave
- Restart Claude Code, then verify with
claude mcp list.
Alternatively, edit the config file directly (~/Library/Application Support/Claude/claude_desktop_config.json on macOS) and add an MCP server entry with the Lockwave URL and Authorization: Bearer YOUR_API_TOKEN in headers.
- From the terminal, add the Lockwave server with HTTP transport and Bearer token:
gemini mcp add --transport http --header "Authorization: Bearer YOUR_API_TOKEN" lockwave https://lockwave.io/mcp/lockwave
Or add the server in .gemini/settings.json or ~/.gemini/settings.json under mcpServers.
- Enable Developer mode in workspace settings (available on Business and Enterprise plans).
- Add an MCP app with the Lockwave server URL and set authentication to use your API token as a Bearer token.
- Only workspace admins or owners can publish MCP apps.
Protocol reference
Configure your MCP client with the URL above and add the Authorization: Bearer YOUR_TOKEN header. For protocol details, see the Model Context Protocol specification.
Security Model
Lockwave's security model is built on these principles:
- Outbound-only — The daemon only makes outbound HTTPS requests. No inbound ports, no SSH tunnels.
- No private keys — We only store public keys. Private keys are generated and delivered once, then discarded.
- Atomic writes — POSIX file locking and rename prevent corrupted authorized_keys files.
- Immutable audit — Every action is logged in an append-only log. Break-glass, drift events, and credential rotations are all recorded.
- Optional SSH hardening — Disable password authentication at the SSH server level via a daemon-managed sshd drop-in so only key-based access is allowed.
- Optional IP binding — Server records the registration IP, which is never automatically updated. On each sync it compares the daemon's IP and logs a mismatch event. You must manually accept a new IP from the host detail page.
Report security issues to security@lockwave.io.
Deploy in Under 5 Minutes
Start free. Run the install script on your first host and see keys sync.