init monlet repo with stage 0-2 (docs, contract, agent MVP)
This commit is contained in:
565
api/openapi.yaml
Normal file
565
api/openapi.yaml
Normal file
@@ -0,0 +1,565 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: Monlet API
|
||||
version: 1.0.0
|
||||
description: |
|
||||
Monlet v1 API contract. Stage 1 frozen.
|
||||
|
||||
All endpoints under `/api/v1`. Authentication uses a single shared Bearer token (see ADR-0007). Agent-facing ingestion endpoints (`/heartbeat`, `/events`) and read-only UI-facing endpoints share the same security scheme in v1.
|
||||
servers:
|
||||
- url: http://127.0.0.1:8000
|
||||
security:
|
||||
- bearerAuth: []
|
||||
paths:
|
||||
/api/v1/health:
|
||||
get:
|
||||
summary: Health check
|
||||
security: []
|
||||
responses:
|
||||
"200":
|
||||
description: Service is alive.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/HealthResponse"
|
||||
/api/v1/ready:
|
||||
get:
|
||||
summary: Readiness check
|
||||
security: []
|
||||
responses:
|
||||
"200":
|
||||
description: Service is ready.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/HealthResponse"
|
||||
"503":
|
||||
description: Service is not ready.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
/api/v1/heartbeat:
|
||||
post:
|
||||
summary: Ingest agent heartbeat
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/HeartbeatRequest"
|
||||
responses:
|
||||
"202":
|
||||
description: Heartbeat accepted.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AcceptedResponse"
|
||||
"400":
|
||||
$ref: "#/components/responses/ValidationError"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"413":
|
||||
$ref: "#/components/responses/PayloadTooLarge"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
/api/v1/events:
|
||||
post:
|
||||
summary: Ingest agent check result events
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EventBatchRequest"
|
||||
responses:
|
||||
"202":
|
||||
description: Events accepted.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EventBatchAcceptedResponse"
|
||||
"400":
|
||||
$ref: "#/components/responses/ValidationError"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"413":
|
||||
$ref: "#/components/responses/PayloadTooLarge"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
/api/v1/agents:
|
||||
get:
|
||||
summary: List agents
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/Cursor"
|
||||
- $ref: "#/components/parameters/Limit"
|
||||
responses:
|
||||
"200":
|
||||
description: Agent list.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/PageEnvelope"
|
||||
- type: object
|
||||
required: [items]
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Agent"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
/api/v1/agents/{agent_id}:
|
||||
get:
|
||||
summary: Get agent detail
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/AgentId"
|
||||
responses:
|
||||
"200":
|
||||
description: Agent detail.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Agent"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"404":
|
||||
$ref: "#/components/responses/NotFound"
|
||||
/api/v1/checks:
|
||||
get:
|
||||
summary: List current check states
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/Cursor"
|
||||
- $ref: "#/components/parameters/Limit"
|
||||
responses:
|
||||
"200":
|
||||
description: Check state list.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/PageEnvelope"
|
||||
- type: object
|
||||
required: [items]
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/CheckState"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
/api/v1/incidents:
|
||||
get:
|
||||
summary: List incidents
|
||||
parameters:
|
||||
- name: state
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
enum: [open, resolved]
|
||||
- $ref: "#/components/parameters/Cursor"
|
||||
- $ref: "#/components/parameters/Limit"
|
||||
responses:
|
||||
"200":
|
||||
description: Incident list.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/PageEnvelope"
|
||||
- type: object
|
||||
required: [items]
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Incident"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
/api/v1/events/query:
|
||||
get:
|
||||
summary: Query stored events
|
||||
parameters:
|
||||
- name: agent_id
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
maxLength: 128
|
||||
pattern: "^[A-Za-z0-9._:-]+$"
|
||||
- name: check_id
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
maxLength: 128
|
||||
pattern: "^[A-Za-z0-9._:-]+$"
|
||||
- $ref: "#/components/parameters/Cursor"
|
||||
- $ref: "#/components/parameters/Limit"
|
||||
responses:
|
||||
"200":
|
||||
description: Event list.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/PageEnvelope"
|
||||
- type: object
|
||||
required: [items]
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/StoredCheckResultEvent"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
/api/v1/notifiers/outbox:
|
||||
get:
|
||||
summary: List notification outbox items
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/Cursor"
|
||||
- $ref: "#/components/parameters/Limit"
|
||||
responses:
|
||||
"200":
|
||||
description: Outbox list.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/PageEnvelope"
|
||||
- type: object
|
||||
required: [items]
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/NotificationOutboxItem"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
description: Shared static Bearer token in v1. See ADR-0007.
|
||||
parameters:
|
||||
AgentId:
|
||||
name: agent_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
maxLength: 128
|
||||
pattern: "^[A-Za-z0-9._:-]+$"
|
||||
Cursor:
|
||||
name: cursor
|
||||
in: query
|
||||
required: false
|
||||
description: Opaque pagination cursor returned in `next_cursor`.
|
||||
schema:
|
||||
type: string
|
||||
maxLength: 512
|
||||
Limit:
|
||||
name: limit
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 1
|
||||
maximum: 500
|
||||
default: 100
|
||||
responses:
|
||||
Unauthorized:
|
||||
description: Authentication failed.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
NotFound:
|
||||
description: Resource not found.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
PayloadTooLarge:
|
||||
description: Payload exceeds configured limit (1 MiB).
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
ValidationError:
|
||||
description: Request body or parameters failed validation.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
InternalError:
|
||||
description: Unexpected server error.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
schemas:
|
||||
HealthResponse:
|
||||
type: object
|
||||
required: [status]
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum: [ok]
|
||||
ErrorResponse:
|
||||
type: object
|
||||
required: [error]
|
||||
properties:
|
||||
error:
|
||||
type: object
|
||||
required: [code, message, request_id]
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
enum:
|
||||
- unauthorized
|
||||
- not_found
|
||||
- validation
|
||||
- payload_too_large
|
||||
- rate_limited
|
||||
- internal
|
||||
- not_ready
|
||||
message:
|
||||
type: string
|
||||
request_id:
|
||||
type: string
|
||||
description: Server-generated request id, mirrored from X-Request-Id.
|
||||
PageEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
next_cursor:
|
||||
type: [string, "null"]
|
||||
description: Pass back as `cursor` to fetch the next page. Null/absent if no more results.
|
||||
AcceptedResponse:
|
||||
type: object
|
||||
required: [accepted]
|
||||
properties:
|
||||
accepted:
|
||||
type: boolean
|
||||
EventBatchAcceptedResponse:
|
||||
type: object
|
||||
required: [accepted, deduplicated]
|
||||
properties:
|
||||
accepted:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: Number of events stored on this request.
|
||||
deduplicated:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: Number of events whose `event_id` was already known.
|
||||
HeartbeatRequest:
|
||||
type: object
|
||||
required: [agent_id, observed_at, hostname, version]
|
||||
properties:
|
||||
agent_id:
|
||||
type: string
|
||||
maxLength: 128
|
||||
pattern: "^[A-Za-z0-9._:-]+$"
|
||||
observed_at:
|
||||
type: string
|
||||
format: date-time
|
||||
hostname:
|
||||
type: string
|
||||
maxLength: 253
|
||||
version:
|
||||
type: string
|
||||
maxLength: 64
|
||||
mode:
|
||||
type: string
|
||||
enum: [prometheus_only, push_only, hybrid]
|
||||
labels:
|
||||
type: object
|
||||
maxProperties: 32
|
||||
propertyNames:
|
||||
maxLength: 64
|
||||
pattern: "^[A-Za-z0-9._:-]+$"
|
||||
additionalProperties:
|
||||
type: string
|
||||
maxLength: 256
|
||||
description: Up to 32 labels. Key max 64, value max 256.
|
||||
EventBatchRequest:
|
||||
type: object
|
||||
required: [agent_id, events]
|
||||
properties:
|
||||
agent_id:
|
||||
type: string
|
||||
maxLength: 128
|
||||
pattern: "^[A-Za-z0-9._:-]+$"
|
||||
events:
|
||||
type: array
|
||||
minItems: 1
|
||||
maxItems: 200
|
||||
items:
|
||||
$ref: "#/components/schemas/CheckResultEvent"
|
||||
CheckResultEvent:
|
||||
type: object
|
||||
description: |
|
||||
Event body used inside `EventBatchRequest.events`. The owning `agent_id`
|
||||
is supplied once at the batch envelope; per-event `agent_id` is intentionally
|
||||
absent so requests cannot mix or spoof other agents under a shared token.
|
||||
Server response endpoints (`GET /events/query`) include `agent_id` separately
|
||||
in their item schema.
|
||||
required:
|
||||
- event_id
|
||||
- check_id
|
||||
- observed_at
|
||||
- status
|
||||
- exit_code
|
||||
- duration_ms
|
||||
properties:
|
||||
event_id:
|
||||
type: string
|
||||
description: UUIDv7 string (lower-case, with dashes). See ADR-0006.
|
||||
minLength: 36
|
||||
maxLength: 36
|
||||
pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
check_id:
|
||||
type: string
|
||||
maxLength: 128
|
||||
pattern: "^[A-Za-z0-9._:-]+$"
|
||||
observed_at:
|
||||
type: string
|
||||
format: date-time
|
||||
status:
|
||||
type: string
|
||||
enum: [ok, warning, critical, unknown]
|
||||
exit_code:
|
||||
type: integer
|
||||
duration_ms:
|
||||
type: integer
|
||||
minimum: 0
|
||||
output:
|
||||
type: string
|
||||
maxLength: 8192
|
||||
description: |
|
||||
Limit is 8 KiB measured in UTF-8 **bytes** (not characters). Agent truncates
|
||||
on a UTF-8 boundary before send and appends marker `...[truncated N bytes]`.
|
||||
`maxLength: 8192` here is a coarse schema-level upper bound; servers MUST
|
||||
additionally reject events whose UTF-8 byte length exceeds 8192 with
|
||||
`400 validation`.
|
||||
output_truncated:
|
||||
type: boolean
|
||||
default: false
|
||||
notification_owner:
|
||||
type: string
|
||||
enum: [server, prometheus, none]
|
||||
incident_key:
|
||||
type: string
|
||||
maxLength: 256
|
||||
StoredCheckResultEvent:
|
||||
description: Stored event returned by `GET /events/query`. Includes the resolved `agent_id` and server-side `received_at`.
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/CheckResultEvent"
|
||||
- type: object
|
||||
required: [agent_id, received_at]
|
||||
properties:
|
||||
agent_id:
|
||||
type: string
|
||||
maxLength: 128
|
||||
pattern: "^[A-Za-z0-9._:-]+$"
|
||||
received_at:
|
||||
type: string
|
||||
format: date-time
|
||||
Agent:
|
||||
type: object
|
||||
required: [agent_id, hostname, status, last_seen_at]
|
||||
properties:
|
||||
agent_id:
|
||||
type: string
|
||||
hostname:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
enum: [alive, stale, dead]
|
||||
last_seen_at:
|
||||
type: string
|
||||
format: date-time
|
||||
version:
|
||||
type: string
|
||||
mode:
|
||||
type: string
|
||||
enum: [prometheus_only, push_only, hybrid]
|
||||
labels:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
CheckState:
|
||||
type: object
|
||||
required: [agent_id, check_id, status, last_observed_at]
|
||||
properties:
|
||||
agent_id:
|
||||
type: string
|
||||
check_id:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
enum: [ok, warning, critical, unknown]
|
||||
last_observed_at:
|
||||
type: string
|
||||
format: date-time
|
||||
exit_code:
|
||||
type: integer
|
||||
incident_key:
|
||||
type: string
|
||||
Incident:
|
||||
type: object
|
||||
required: [id, incident_key, state, severity, opened_at]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
incident_key:
|
||||
type: string
|
||||
state:
|
||||
type: string
|
||||
enum: [open, resolved]
|
||||
severity:
|
||||
type: string
|
||||
enum: [warning, critical, unknown]
|
||||
agent_id:
|
||||
type: string
|
||||
check_id:
|
||||
type: string
|
||||
opened_at:
|
||||
type: string
|
||||
format: date-time
|
||||
resolved_at:
|
||||
type: [string, "null"]
|
||||
format: date-time
|
||||
summary:
|
||||
type: string
|
||||
NotificationOutboxItem:
|
||||
type: object
|
||||
required: [id, notifier, state, incident_id, event_type, attempts]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
notifier:
|
||||
type: string
|
||||
state:
|
||||
type: string
|
||||
enum: [pending, sending, sent, retry, failed, discarded]
|
||||
incident_id:
|
||||
type: string
|
||||
event_type:
|
||||
type: string
|
||||
enum: [firing, resolved]
|
||||
attempts:
|
||||
type: integer
|
||||
minimum: 0
|
||||
next_attempt_at:
|
||||
type: [string, "null"]
|
||||
format: date-time
|
||||
last_error:
|
||||
type: [string, "null"]
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
Reference in New Issue
Block a user