mirror of
https://github.com/juanfont/headscale.git
synced 2026-08-07 15:58:45 +09:00
f6aea642d3
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.
724 lines
22 KiB
YAML
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 }
|