Files
headscale/openapi/v1/headscale.yaml
T
Kristoffer Dalby f6aea642d3 all: scrub stale gRPC references from comments and docs
Reword comments and docs that still named the removed gRPC stack, rename
the GRPCSortable test interface, and drop the dead gRPC port note from the
dev README.
2026-06-19 05:58:10 +00:00

724 lines
22 KiB
YAML

openapi: 3.0.3
info:
title: Headscale API
version: "1"
description: |
HTTP API for managing a Headscale control server: users, pre-auth keys,
nodes, API keys, and ACL policy. This is the v1 API, served under
`/api/v1`. It is the OpenAPI 3.0 definition and the single source of truth
for the API contract.
Authentication is a bearer API key (`Authorization: Bearer <key>`),
except over the local unix socket where filesystem permissions are the
trust boundary.
servers:
- url: /
security:
- bearerAuth: []
tags:
- name: Users
- name: PreAuthKeys
- name: Nodes
- name: Auth
- name: ApiKeys
- name: Policy
- name: Health
paths:
/api/v1/user:
post:
operationId: CreateUser
tags: [Users]
summary: Create a user.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
name: { type: string }
displayName: { type: string }
email: { type: string }
pictureUrl: { type: string }
responses:
"200":
description: The created user.
content:
application/json:
schema:
type: object
properties:
user: { $ref: "#/components/schemas/User" }
default: { $ref: "#/components/responses/Error" }
get:
operationId: ListUsers
tags: [Users]
summary: List users, optionally filtered by id, name, or email.
parameters:
- { name: id, in: query, required: false, schema: { type: integer, format: uint64 } }
- { name: name, in: query, required: false, schema: { type: string } }
- { name: email, in: query, required: false, schema: { type: string } }
responses:
"200":
description: Matching users, sorted by id.
content:
application/json:
schema:
type: object
properties:
users:
type: array
items: { $ref: "#/components/schemas/User" }
default: { $ref: "#/components/responses/Error" }
/api/v1/user/{old_id}/rename/{new_name}:
post:
operationId: RenameUser
tags: [Users]
summary: Rename a user.
parameters:
- { name: old_id, in: path, required: true, schema: { type: integer, format: uint64 } }
- { name: new_name, in: path, required: true, schema: { type: string } }
responses:
"200":
description: The renamed user.
content:
application/json:
schema:
type: object
properties:
user: { $ref: "#/components/schemas/User" }
default: { $ref: "#/components/responses/Error" }
/api/v1/user/{id}:
delete:
operationId: DeleteUser
tags: [Users]
summary: Delete a user.
parameters:
- { name: id, in: path, required: true, schema: { type: integer, format: uint64 } }
responses:
"200":
description: User deleted.
content:
application/json:
schema: { type: object }
default: { $ref: "#/components/responses/Error" }
/api/v1/preauthkey:
post:
operationId: CreatePreAuthKey
tags: [PreAuthKeys]
summary: Create a pre-auth key.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
user: { type: integer, format: uint64 }
reusable: { type: boolean }
ephemeral: { type: boolean }
expiration: { type: string, format: date-time }
aclTags:
type: array
items: { type: string }
responses:
"200":
description: The created pre-auth key.
content:
application/json:
schema:
type: object
properties:
preAuthKey: { $ref: "#/components/schemas/PreAuthKey" }
default: { $ref: "#/components/responses/Error" }
get:
operationId: ListPreAuthKeys
tags: [PreAuthKeys]
summary: List all pre-auth keys.
responses:
"200":
description: Pre-auth keys, sorted by id.
content:
application/json:
schema:
type: object
properties:
preAuthKeys:
type: array
items: { $ref: "#/components/schemas/PreAuthKey" }
default: { $ref: "#/components/responses/Error" }
delete:
operationId: DeletePreAuthKey
tags: [PreAuthKeys]
summary: Delete a pre-auth key.
parameters:
- { name: id, in: query, required: false, schema: { type: integer, format: uint64 } }
responses:
"200":
description: Pre-auth key deleted.
content:
application/json:
schema: { type: object }
default: { $ref: "#/components/responses/Error" }
/api/v1/preauthkey/expire:
post:
operationId: ExpirePreAuthKey
tags: [PreAuthKeys]
summary: Expire a pre-auth key.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
id: { type: integer, format: uint64 }
responses:
"200":
description: Pre-auth key expired.
content:
application/json:
schema: { type: object }
default: { $ref: "#/components/responses/Error" }
/api/v1/node:
get:
operationId: ListNodes
tags: [Nodes]
summary: List nodes, optionally filtered by user.
parameters:
- { name: user, in: query, required: false, schema: { type: string } }
responses:
"200":
description: Matching nodes, sorted by id.
content:
application/json:
schema:
type: object
properties:
nodes:
type: array
items: { $ref: "#/components/schemas/Node" }
default: { $ref: "#/components/responses/Error" }
/api/v1/node/register:
post:
operationId: RegisterNode
tags: [Nodes]
summary: Register a node to a user using a registration id.
parameters:
- { name: user, in: query, required: false, schema: { type: string } }
- { name: key, in: query, required: false, schema: { type: string } }
responses:
"200":
description: The registered node.
content:
application/json:
schema:
type: object
properties:
node: { $ref: "#/components/schemas/Node" }
default: { $ref: "#/components/responses/Error" }
/api/v1/node/backfillips:
post:
operationId: BackfillNodeIPs
tags: [Nodes]
summary: Backfill missing IP addresses for all nodes.
parameters:
- { name: confirmed, in: query, required: false, schema: { type: boolean } }
responses:
"200":
description: List of changes made.
content:
application/json:
schema:
type: object
properties:
changes:
type: array
items: { type: string }
default: { $ref: "#/components/responses/Error" }
/api/v1/node/{node_id}:
get:
operationId: GetNode
tags: [Nodes]
summary: Get a node by id.
parameters:
- { name: node_id, in: path, required: true, schema: { type: integer, format: uint64 } }
responses:
"200":
description: The node.
content:
application/json:
schema:
type: object
properties:
node: { $ref: "#/components/schemas/Node" }
default: { $ref: "#/components/responses/Error" }
delete:
operationId: DeleteNode
tags: [Nodes]
summary: Delete a node.
parameters:
- { name: node_id, in: path, required: true, schema: { type: integer, format: uint64 } }
responses:
"200":
description: Node deleted.
content:
application/json:
schema: { type: object }
default: { $ref: "#/components/responses/Error" }
/api/v1/node/{node_id}/tags:
post:
operationId: SetTags
tags: [Nodes]
summary: Set the tags of a node (converts it to a tagged node).
parameters:
- { name: node_id, in: path, required: true, schema: { type: integer, format: uint64 } }
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
tags:
type: array
items: { type: string }
responses:
"200":
description: The updated node.
content:
application/json:
schema:
type: object
properties:
node: { $ref: "#/components/schemas/Node" }
default: { $ref: "#/components/responses/Error" }
/api/v1/node/{node_id}/approve_routes:
post:
operationId: SetApprovedRoutes
tags: [Nodes]
summary: Set the approved subnet routes of a node.
parameters:
- { name: node_id, in: path, required: true, schema: { type: integer, format: uint64 } }
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
routes:
type: array
items: { type: string }
responses:
"200":
description: The updated node.
content:
application/json:
schema:
type: object
properties:
node: { $ref: "#/components/schemas/Node" }
default: { $ref: "#/components/responses/Error" }
/api/v1/node/{node_id}/expire:
post:
operationId: ExpireNode
tags: [Nodes]
summary: Expire a node, or disable its expiry.
parameters:
- { name: node_id, in: path, required: true, schema: { type: integer, format: uint64 } }
- { name: expiry, in: query, required: false, schema: { type: string, format: date-time } }
- { name: disable_expiry, in: query, required: false, schema: { type: boolean } }
responses:
"200":
description: The updated node.
content:
application/json:
schema:
type: object
properties:
node: { $ref: "#/components/schemas/Node" }
default: { $ref: "#/components/responses/Error" }
/api/v1/node/{node_id}/rename/{new_name}:
post:
operationId: RenameNode
tags: [Nodes]
summary: Rename a node.
parameters:
- { name: node_id, in: path, required: true, schema: { type: integer, format: uint64 } }
- { name: new_name, in: path, required: true, schema: { type: string } }
responses:
"200":
description: The renamed node.
content:
application/json:
schema:
type: object
properties:
node: { $ref: "#/components/schemas/Node" }
default: { $ref: "#/components/responses/Error" }
/api/v1/debug/node:
post:
operationId: DebugCreateNode
tags: [Nodes]
summary: Create a debug node and cache its registration (testing only).
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
user: { type: string }
key: { type: string }
name: { type: string }
routes:
type: array
items: { type: string }
responses:
"200":
description: A synthetic node echoing the requested registration.
content:
application/json:
schema:
type: object
properties:
node: { $ref: "#/components/schemas/Node" }
default: { $ref: "#/components/responses/Error" }
/api/v1/auth/register:
post:
operationId: AuthRegister
tags: [Auth]
summary: Register a node via an auth id (alias of RegisterNode).
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
user: { type: string }
authId: { type: string }
responses:
"200":
description: The registered node.
content:
application/json:
schema:
type: object
properties:
node: { $ref: "#/components/schemas/Node" }
default: { $ref: "#/components/responses/Error" }
/api/v1/auth/approve:
post:
operationId: AuthApprove
tags: [Auth]
summary: Approve a pending auth session.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
authId: { type: string }
responses:
"200":
description: Auth session approved.
content:
application/json:
schema: { type: object }
default: { $ref: "#/components/responses/Error" }
/api/v1/auth/reject:
post:
operationId: AuthReject
tags: [Auth]
summary: Reject a pending auth session.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
authId: { type: string }
responses:
"200":
description: Auth session rejected.
content:
application/json:
schema: { type: object }
default: { $ref: "#/components/responses/Error" }
/api/v1/apikey:
post:
operationId: CreateApiKey
tags: [ApiKeys]
summary: Create an API key.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
expiration: { type: string, format: date-time }
responses:
"200":
description: The created API key (the only time the full key is returned).
content:
application/json:
schema:
type: object
properties:
apiKey: { type: string }
default: { $ref: "#/components/responses/Error" }
get:
operationId: ListApiKeys
tags: [ApiKeys]
summary: List all API keys.
responses:
"200":
description: API keys, sorted by id.
content:
application/json:
schema:
type: object
properties:
apiKeys:
type: array
items: { $ref: "#/components/schemas/ApiKey" }
default: { $ref: "#/components/responses/Error" }
/api/v1/apikey/expire:
post:
operationId: ExpireApiKey
tags: [ApiKeys]
summary: Expire an API key by id or prefix.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
prefix: { type: string }
id: { type: integer, format: uint64 }
responses:
"200":
description: API key expired.
content:
application/json:
schema: { type: object }
default: { $ref: "#/components/responses/Error" }
/api/v1/apikey/{prefix}:
delete:
operationId: DeleteApiKey
tags: [ApiKeys]
summary: Delete an API key by prefix (or id).
parameters:
- { name: prefix, in: path, required: true, schema: { type: string } }
- { name: id, in: query, required: false, schema: { type: integer, format: uint64 } }
responses:
"200":
description: API key deleted.
content:
application/json:
schema: { type: object }
default: { $ref: "#/components/responses/Error" }
/api/v1/policy:
get:
operationId: GetPolicy
tags: [Policy]
summary: Get the current ACL policy.
responses:
"200":
description: The current policy.
content:
application/json:
schema:
type: object
properties:
policy: { type: string }
updatedAt: { type: string, format: date-time }
default: { $ref: "#/components/responses/Error" }
put:
operationId: SetPolicy
tags: [Policy]
summary: Set the ACL policy (database policy mode only).
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
policy: { type: string }
responses:
"200":
description: The stored policy.
content:
application/json:
schema:
type: object
properties:
policy: { type: string }
updatedAt: { type: string, format: date-time }
default: { $ref: "#/components/responses/Error" }
/api/v1/policy/check:
post:
operationId: CheckPolicy
tags: [Policy]
summary: Validate a policy against live users and nodes without storing it.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
policy: { type: string }
responses:
"200":
description: Policy is valid.
content:
application/json:
schema: { type: object }
default: { $ref: "#/components/responses/Error" }
/api/v1/health:
get:
operationId: Health
tags: [Health]
summary: Report server health, including database connectivity.
responses:
"200":
description: Health status.
content:
application/json:
schema:
type: object
properties:
databaseConnectivity: { type: boolean }
default: { $ref: "#/components/responses/Error" }
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
responses:
Error:
description: Error, as an RFC 7807 problem document.
content:
application/problem+json:
schema: { $ref: "#/components/schemas/Problem" }
schemas:
Problem:
type: object
description: RFC 7807 problem details.
properties:
type: { type: string, format: uri }
title: { type: string }
status: { type: integer, format: int32 }
detail: { type: string }
instance: { type: string, format: uri }
RegisterMethod:
type: string
enum:
- REGISTER_METHOD_UNSPECIFIED
- REGISTER_METHOD_AUTH_KEY
- REGISTER_METHOD_CLI
- REGISTER_METHOD_OIDC
User:
type: object
properties:
id: { type: integer, format: uint64 }
name: { type: string }
createdAt: { type: string, format: date-time }
displayName: { type: string }
email: { type: string }
providerId: { type: string }
provider: { type: string }
profilePicUrl: { type: string }
PreAuthKey:
type: object
properties:
user: { $ref: "#/components/schemas/User" }
id: { type: integer, format: uint64 }
key: { type: string }
reusable: { type: boolean }
ephemeral: { type: boolean }
used: { type: boolean }
expiration: { type: string, format: date-time }
createdAt: { type: string, format: date-time }
aclTags:
type: array
items: { type: string }
ApiKey:
type: object
properties:
id: { type: integer, format: uint64 }
prefix: { type: string }
expiration: { type: string, format: date-time }
createdAt: { type: string, format: date-time }
lastSeen: { type: string, format: date-time }
Node:
type: object
properties:
id: { type: integer, format: uint64 }
machineKey: { type: string }
nodeKey: { type: string }
discoKey: { type: string }
ipAddresses:
type: array
items: { type: string }
name: { type: string }
user: { $ref: "#/components/schemas/User" }
lastSeen: { type: string, format: date-time }
expiry: { type: string, format: date-time }
preAuthKey: { $ref: "#/components/schemas/PreAuthKey" }
createdAt: { type: string, format: date-time }
registerMethod: { $ref: "#/components/schemas/RegisterMethod" }
givenName: { type: string }
online: { type: boolean }
approvedRoutes:
type: array
items: { type: string }
availableRoutes:
type: array
items: { type: string }
subnetRoutes:
type: array
items: { type: string }
tags:
type: array
items: { type: string }