mirror of
https://github.com/juanfont/headscale.git
synced 2026-08-07 15:58:45 +09:00
38dad226c8
Single source of truth for the v1 HTTP API: 27 operations, native int64 ids, RFC 7807 problem responses, bearer auth.
725 lines
22 KiB
YAML
725 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 successor to the previous
|
|
gRPC/grpc-gateway facade and is 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 }
|