2025-05-03 18:26:12 +03:00

360 lines
13 KiB
Markdown

# MiniDiscovery API Documentation
This document describes the HTTP API endpoints provided by MiniDiscovery.
## Authentication
Most API endpoints require authentication using an API token. Tokens must be passed in the `X-API-Token` HTTP header.
```http
GET /v1/catalog/services HTTP/1.1
Host: your-minidiscovery-host:8500
X-API-Token: your_secret_api_token
```
Tokens have associated permissions:
* **`read`**: Allows reading service information (catalog, health).
* **`write`**: Allows registering and deregistering services.
* **`admin`**: Allows all `read` and `write` operations, plus managing ACL tokens.
The initial admin token is set using the `MINIDISCOVERY_ADMIN_TOKEN` environment variable on the *first* run when the database is empty. Subsequent tokens are managed via the API.
## Data Models
### `ServiceInstance`
This model represents a single instance of a registered service.
```json
{
"id": "web-instance-1",
"name": "web",
"address": "192.168.1.100",
"port": 80,
"tags": ["frontend", "nginx"],
"metadata": {
"version": "1.2.3",
"region": "us-east-1"
},
"health": "passing",
"last_updated": 1678886400.123456
}
```
* `id` (string, required): Unique identifier for this specific instance.
* `name` (string, required): Logical name of the service (e.g., 'web', 'api', 'db').
* `address` (string, required): IP address or resolvable hostname where the service listens.
* `port` (integer, required): Port number (1-65535).
* `tags` (array of strings, optional): List of tags for filtering/grouping. Defaults to `[]`.
* `metadata` (object, optional): Key-value string pairs for arbitrary metadata. Defaults to `{}`.
* `health` (string, optional): Current health status ('passing', 'failing', 'unknown'). Defaults to 'passing' on registration. Automatically updated by the health checker.
* `last_updated` (float, internal): Unix timestamp of the last update (set automatically).
## Endpoints
### Agent Endpoints
These endpoints interact with the local agent state (registering/deregistering services).
#### Register Service
* **`POST /v1/agent/service/register`**
* **Description:** Registers a new service instance or updates an existing one with the same `id`. This operation is idempotent based on the service `id`.
* **Authentication:** Requires `write` permission.
* **Request Body:** `ServiceInstance` JSON object. The `health` and `last_updated` fields in the request body are ignored; health is managed internally, and `last_updated` is set automatically.
* **Success Response:** `200 OK`
```json
{
"status": "registered",
"service_id": "web-instance-1"
}
```
* **Error Responses:**
* `400 Bad Request`: Invalid request body format.
* `401 Unauthorized`: Missing `X-API-Token`.
* `403 Forbidden`: Invalid token or insufficient permissions.
* `500 Internal Server Error`: Database error during registration.
* **`curl` Example:**
```bash
curl -X POST http://localhost:8500/v1/agent/service/register \
-H "X-API-Token: your_write_token" \
-H "Content-Type: application/json" \
-d '{
"id": "api-instance-01",
"name": "api",
"address": "10.0.1.5",
"port": 8080,
"tags": ["backend", "v2"],
"metadata": {"git_sha": "a1b2c3d"}
}'
```
#### Deregister Service
* **`PUT /v1/agent/service/deregister/{service_id}`**
*(Note: Consul uses PUT here, although DELETE might feel more intuitive)*
* **Description:** Removes a service instance by its ID.
* **Authentication:** Requires `write` permission.
* **Path Parameters:**
* `service_id` (string): The unique ID of the service instance to deregister.
* **Success Response:** `200 OK`
```json
{
"status": "deregistered",
"service_id": "api-instance-01"
}
```
* **Error Responses:**
* `401 Unauthorized`: Missing `X-API-Token`.
* `403 Forbidden`: Invalid token or insufficient permissions.
* `404 Not Found`: Service with the given `service_id` does not exist.
* **`curl` Example:**
```bash
curl -X PUT http://localhost:8500/v1/agent/service/deregister/api-instance-01 \
-H "X-API-Token: your_write_token"
```
### Catalog Endpoints
These endpoints provide information about registered services across the system.
#### List Services
* **`GET /v1/catalog/services`**
* **Description:** Returns a map of all registered service names to a list of unique tags associated with instances of that service.
* **Authentication:** Requires `read` permission.
* **Success Response:** `200 OK`
```json
{
"web": ["frontend", "nginx"],
"api": ["backend", "v2"],
"db": []
}
```
* **Error Responses:**
* `401 Unauthorized`: Missing `X-API-Token`.
* `403 Forbidden`: Invalid token or insufficient permissions.
* **`curl` Example:**
```bash
curl http://localhost:8500/v1/catalog/services \
-H "X-API-Token: your_read_token"
```
#### List Service Instances
* **`GET /v1/catalog/service/{service_name}`**
* **Description:** Returns a list of all registered instances for a given service name.
* **Authentication:** Requires `read` permission.
* **Path Parameters:**
* `service_name` (string): The name of the service (e.g., 'web', 'api').
* **Query Parameters:**
* `tag` (string, optional): Filter instances by tag. Only instances with this tag will be returned.
* **Success Response:** `200 OK` (Returns an array of `ServiceInstance` objects)
```json
[
{
"id": "web-instance-1",
"name": "web",
"address": "192.168.1.100",
"port": 80,
"tags": ["frontend", "nginx"],
"metadata": {"version": "1.2.3"},
"health": "passing",
"last_updated": 1678886400.123
},
{
"id": "web-instance-2",
"name": "web",
"address": "192.168.1.101",
"port": 80,
"tags": ["frontend"],
"metadata": {"version": "1.2.4"},
"health": "failing",
"last_updated": 1678886405.456
}
]
```
* Returns `[]` if the service name is not found.
* **Error Responses:**
* `401 Unauthorized`: Missing `X-API-Token`.
* `403 Forbidden`: Invalid token or insufficient permissions.
* **`curl` Examples:**
```bash
# Get all 'web' instances
curl http://localhost:8500/v1/catalog/service/web \
-H "X-API-Token: your_read_token"
# Get 'web' instances tagged with 'nginx'
curl http://localhost:8500/v1/catalog/service/web?tag=nginx \
-H "X-API-Token: your_read_token"
```
### Health Endpoints
These endpoints provide service discovery filtered by health status.
#### List Healthy Service Instances
* **`GET /v1/health/service/{service_name}`**
* **Description:** Returns a list of service instances, similar to the catalog endpoint, but allows filtering by health status.
* **Authentication:** Requires `read` permission.
* **Path Parameters:**
* `service_name` (string): The name of the service.
* **Query Parameters:**
* `tag` (string, optional): Filter instances by tag.
* `passing` (boolean, optional): If `true`, only return instances with a `passing` health status. Defaults to `false` (returns all instances regardless of health).
* **Success Response:** `200 OK` (Returns an array of `ServiceInstance` objects)
* Response format is identical to `/v1/catalog/service/{service_name}` but filtered according to query parameters.
* **Error Responses:**
* `401 Unauthorized`: Missing `X-API-Token`.
* `403 Forbidden`: Invalid token or insufficient permissions.
* **`curl` Examples:**
```bash
# Get all 'api' instances (healthy or not)
curl http://localhost:8500/v1/health/service/api \
-H "X-API-Token: your_read_token"
# Get only healthy ('passing') 'api' instances
curl http://localhost:8500/v1/health/service/api?passing=true \
-H "X-API-Token: your_read_token"
# Get only healthy 'api' instances tagged 'v2'
curl 'http://localhost:8500/v1/health/service/api?passing=true&tag=v2' \
-H "X-API-Token: your_read_token"
```
### ACL Token Endpoints
These endpoints manage API access tokens.
#### Create Token
* **`POST /v1/acl/token`**
* **Description:** Creates a new API token with specified permissions.
* **Authentication:** Requires `admin` permission.
* **Request Body:**
```json
{
"name": "my-app-token",
"permissions": ["read", "write"]
}
```
* `name` (string, required): A unique descriptive name for the token.
* `permissions` (array of strings, optional): List of permissions (`read`, `write`, `admin`). Defaults to `["read", "write"]`.
* **Success Response:** `201 Created`
```json
{
"token": "YOUR_NEW_SECRET_TOKEN_VALUE",
"name": "my-app-token",
"permissions": ["read", "write"]
}
```
* **IMPORTANT:** The `token` value is the actual secret token. It is **only shown once** in this response. Store it securely immediately.
* **Error Responses:**
* `400 Bad Request`: Invalid request body or token name already exists.
* `401 Unauthorized`: Missing `X-API-Token`.
* `403 Forbidden`: Invalid token or insufficient permissions (not admin).
* `500 Internal Server Error`: Database error during token creation.
* **`curl` Example:**
```bash
curl -X POST http://localhost:8500/v1/acl/token \
-H "X-API-Token: your_admin_token" \
-H "Content-Type: application/json" \
-d '{
"name": "read-only-dashboard",
"permissions": ["read"]
}'
```
#### Revoke Token
* **`DELETE /v1/acl/token/{token_to_revoke}`**
* **Description:** Revokes (deletes) an existing API token.
* **Authentication:** Requires `admin` permission.
* **Path Parameters:**
* `token_to_revoke` (string): The **actual secret token value** of the token you want to revoke.
* **Success Response:** `200 OK`
```json
{
"status": "revoked",
"token_info": "Token revoked successfully"
}
```
* **Error Responses:**
* `401 Unauthorized`: Missing `X-API-Token`.
* `403 Forbidden`: Invalid token or insufficient permissions (not admin).
* `404 Not Found`: The token specified in the path does not exist or was already revoked.
* **`curl` Example:**
```bash
# Replace YOUR_OLD_SECRET_TOKEN_VALUE with the token to be deleted
curl -X DELETE http://localhost:8500/v1/acl/token/YOUR_OLD_SECRET_TOKEN_VALUE \
-H "X-API-Token: your_admin_token"
```
### DNS over HTTPS (DoH)
* **`GET /dns-query`**
* **Description:** Provides a simplified DNS-over-HTTPS interface (RFC 8484 GET format) for querying service addresses and SRV records. Primarily intended for simple DNS clients or scripts. **Note:** Requires service names to end with the configured DNS suffix (default: `.laiska.local`).
* **Authentication:** None (typically).
* **Query Parameters:**
* `name` (string, required): The DNS query name (e.g., `web.laiska.local`, `_frontend._tcp.web.laiska.local`).
* `type` (string, optional): The DNS record type (e.g., `A`, `SRV`, `TXT`). Defaults to `A`. Case-insensitive.
* **Success Response:** `200 OK` (JSON object following RFC 8484 structure)
* **Example (A query for `web.laiska.local`):**
```json
{
"Status": 0, "TC": false, "RD": true, "RA": false, "AD": false, "CD": false,
"Question": [{"name": "web.laiska.local.", "type": 1}],
"Answer": [
{"name": "web.laiska.local.", "type": 1, "TTL": 60, "data": "192.168.1.100"},
{"name": "web.laiska.local.", "type": 1, "TTL": 60, "data": "192.168.1.101"}
]
}
```
* **Example (SRV query for `_frontend._tcp.web.laiska.local`):**
```json
{
"Status": 0, "TC": false, "RD": true, "RA": false, "AD": false, "CD": false,
"Question": [{"name": "_frontend._tcp.web.laiska.local.", "type": 33}],
"Answer": [
{"name": "_frontend._tcp.web.laiska.local.", "type": 33, "TTL": 60, "data": "0 10 80 web-instance-1.laiska.local."},
{"name": "_frontend._tcp.web.laiska.local.", "type": 33, "TTL": 60, "data": "0 10 80 web-instance-2.laiska.local."}
],
"Additional": [ // Note: MiniDiscovery doesn't currently populate Additional well for DoH
// Ideally A records for targets would be here
]
}
```
* **Error/NXDOMAIN Response:** Returns JSON with `"Status"` other than `0` (e.g., `2` for NXDOMAIN, `4` for Not Implemented type).
* **`curl` Example:**
```bash
# Query for A records
curl "http://localhost:8500/dns-query?name=web.laiska.local&type=A"
# Query for SRV records
curl "http://localhost:8500/dns-query?name=_frontend._tcp.web.laiska.local&type=SRV"
```
### Other Endpoints
#### Root
* **`GET /`**
* **Description:** Provides a basic entry point. Redirects browsers to `/status`. API clients receive a simple JSON message.
* **Authentication:** None.
* **Response (Browser):** HTTP 307 Redirect to `/status`.
* **Response (API Client):** `200 OK`
```json
{
"message": "MiniDiscovery API Root. See /status for HTML view or /docs for API documentation."
}
```
#### Status Page
* **`GET /status`**
* **Description:** Serves a simple HTML status page showing node health based on registered services. Useful for a quick visual overview. Automatically refreshes.
* **Authentication:** None.
* **Response:** `200 OK` with `Content-Type: text/html`.