Files
Maestro/spec.yaml
T
Admin 73fd96d698 feat(peers): full trust-store UI — accept, reject, revoke, re-authorize
- /api/peers/reject forwards to the auth service's new /internal/reject
  (pending request denied + audited; peer stays on the no-op path).
- TrustedServices panel now has three sections: Requested (Accept/Reject),
  Accepted (Revoke), and Revoked (Re-authorize), with timestamps.
- The old pending 'Ignore' button mapped to revoke, which was the wrong
  operation for a never-approved request.
2026-08-17 00:27:42 +01:00

572 lines
17 KiB
YAML

openapi: 3.0.3
info:
title: Dcntrlized Maestro API
version: 1.0.0
description: The API of the Maestro orchestration router. Maestro manages the lifecycle of Dcntrlized service containers and routes inter-service action calls to them.
components:
schemas:
ActionRequest:
type: object
properties:
payload:
type: object
description: Action-specific parameters
ActionResponse:
type: object
properties:
status:
type: string
data:
type: object
SuccessResponse:
type: object
properties:
status:
type: string
example: "success"
message:
type: string
PendingAuthorization:
type: object
description: A first-contact authorization request awaiting the instance owner (M2 §3).
properties:
service:
type: string
example: "keepnote"
fingerprint:
type: string
example: "sha256:aa:bb:cc:dd"
first_seen:
type: string
last_seen:
type: string
AuthorizedKey:
type: object
description: An approved peer row of the auth service's trust store.
properties:
service:
type: string
fingerprint:
type: string
approved_by:
type: string
approved_at:
type: string
revoked_at:
type: string
nullable: true
PeerAction:
type: object
required: [service, fingerprint]
properties:
service:
type: string
description: The service name (cert CN / org.dcntrlized.service).
fingerprint:
type: string
description: The SHA256 public-key fingerprint (sha256:…) shown to the owner.
Attestation:
type: object
description: The auth service's public signing key statement (D3).
properties:
public_key:
type: string
fingerprint:
type: string
example: "sha256:…"
kid:
type: string
issued_at:
type: string
RegistryContainer:
type: object
required: [name, image]
properties:
name:
type: string
example: "keepnote"
version:
type: string
example: "0.0.1"
image:
type: string
example: "192.168.122.183:3000/dcntrlized/keepnote:dev"
digest:
type: string
title:
type: string
example: "KeepNote"
vendor:
type: string
author:
type: string
description:
type: string
source:
type: string
revision:
type: string
licenses:
type: string
url:
type: string
documentation:
type: string
created_at:
type: string
hidden:
type: boolean
description: Hidden infrastructure containers (e.g. the shared dcntrlized-db) are excluded from the service grid.
example: true
PortMapping:
type: object
properties:
host:
type: integer
guest:
type: integer
IPAddress:
type: object
properties:
version:
type: string
enum: [v4, v6]
address:
type: string
Network:
type: object
properties:
name:
type: string
ipAddress:
$ref: '#/components/schemas/IPAddress'
mode:
type: string
enum: [bridge, host, none, overlay, ipvlan, macvlan]
ports:
type: array
items:
$ref: '#/components/schemas/PortMapping'
Container:
type: object
properties:
id:
type: string
name:
type: string
service_name:
type: string
role:
type: string
example: "db"
hidden:
type: boolean
description: Hidden infrastructure containers (e.g. the shared dcntrlized-db) are excluded from the service grid.
capabilities:
type: array
items:
type: string
example: "database"
description: Capabilities declared by the service manifest (e.g. database).
has_auth:
type: boolean
description: >-
Whether the service consumes the shared auth service (manifest
has_auth). True for dcntrlized-auth itself and for auth-consuming
services.
public_key:
type: string
description: >-
The auth service's RS256 public key (manifest public_key); the hook
for a future Maestro dashboard auth that validates dcntrlized-auth
tokens.
docker_name:
type: string
image:
type: string
version:
type: string
status:
type: string
created_at:
type: string
network:
$ref: '#/components/schemas/Network'
title:
type: string
vendor:
type: string
author:
type: string
description:
type: string
source:
type: string
paths:
/api/actions/{containerId}/{actionName}:
parameters:
- name: actionName
in: path
required: true
description: The name of the specific action to execute
schema:
type: string
- name: containerId
in: path
required: true
description: The ID or name of the target container/service. Accepts a full or partial container ID, a container name, or the service name (matched case-insensitively).
schema:
type: string
get:
summary: Retrieve data or status from a service action
tags: ["actions"]
description: Forwards the request (method, headers, query string, and body) to the target container's /action/{actionName} endpoint. The target service is responsible for authentication; any client-supplied auth headers are forwarded as-is.
responses:
'200':
description: Request successful
content:
application/json:
schema:
$ref: '#/components/schemas/ActionResponse'
'400':
description: Bad Request - Invalid input parameters
'401':
description: Unauthorized - the target service rejected the request
'404':
description: Action or container not found
'500':
description: Failed to forward the request to the container
post:
summary: Execute a state-changing action in the service
tags: ["actions"]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ActionRequest'
responses:
'200':
description: Action executed successfully
content:
application/json:
schema:
$ref: '#/components/schemas/ActionResponse'
'400':
description: Bad Request - Invalid input parameters
'401':
description: Unauthorized - the target service rejected the request
'404':
description: Action or container not found
'500':
description: Failed to forward the request to the container
put:
summary: Update configuration for a specific action
tags: ["actions"]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ActionRequest'
responses:
'200':
description: Configuration updated
'400':
description: Bad Request - Invalid input parameters
'401':
description: Unauthorized - the target service rejected the request
'404':
description: Action or container not found
'500':
description: Failed to forward the request to the container
delete:
tags: ["actions"]
summary: Reset or remove an action's state
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ActionRequest'
responses:
'200':
description: Action state reset
'400':
description: Bad Request - Invalid input parameters
'401':
description: Unauthorized - the target service rejected the request
'404':
description: Action or container not found
'500':
description: Failed to forward the request to the container
/api/containers:
get:
summary: List the services Maestro manages
tags: ["containers"]
responses:
'200':
description: List of services
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Container'
'500':
description: Internal server error
/api/containers/install:
post:
summary: Install a service container (downloads the compose file and pulls the image)
tags: ["containers"]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegistryContainer'
responses:
'200':
description: Container downloaded and image pulled successfully
content:
application/json:
schema:
$ref: '#/components/schemas/SuccessResponse'
'400':
description: Invalid JSON payload or missing required fields (name, image)
'500':
description: Failed to install container
/api/containers/uninstall:
post:
summary: Uninstall a service container and remove it from the cache
tags: ["containers"]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name]
properties:
name:
type: string
responses:
'200':
description: Container uninstalled successfully
content:
application/json:
schema:
$ref: '#/components/schemas/SuccessResponse'
'400':
description: Invalid JSON payload or missing required field (name)
'500':
description: Failed to uninstall container
/api/containers/start:
post:
summary: Start a previously installed service container
tags: ["containers"]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name]
properties:
name:
type: string
responses:
'200':
description: Container started successfully
content:
application/json:
schema:
$ref: '#/components/schemas/SuccessResponse'
'400':
description: Invalid JSON payload or missing required field (name)
'500':
description: Failed to start container
/api/containers/stop:
post:
summary: Stop a running service container
tags: ["containers"]
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [name]
properties:
name:
type: string
responses:
'200':
description: Container stopped successfully
content:
application/json:
schema:
$ref: '#/components/schemas/SuccessResponse'
'400':
description: Invalid JSON payload or missing required field (name)
'500':
description: Failed to stop container
/api/registry:
get:
summary: List the services available in the Dcntrlized registry
tags: ["registry"]
responses:
'200':
description: List of registry services
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/RegistryContainer'
'500':
description: Internal server error
'502':
description: Failed to connect to upstream registry
/api/ws:
get:
summary: WebSocket endpoint for live container events
tags: ["containers"]
description: Establishes a WebSocket connection that streams container lifecycle events as JSON.
responses:
'101':
description: Switching Protocols - WebSocket upgrade
/api/peers:
get:
summary: List pending authorizations and the approved peer trust store
tags: ["security"]
description: >
Security theme M2 §3. Returns the shared auth service's first-contact
authorization requests (services whose certificate is valid but has
not been approved by the instance owner yet) and the approved keys.
Maestro is only the UI; the auth service owns the enforcement.
responses:
'200':
description: Combined pending + approved trust state
content:
application/json:
schema:
type: object
properties:
pending:
type: array
items:
$ref: '#/components/schemas/PendingAuthorization'
authorized:
type: array
items:
$ref: '#/components/schemas/AuthorizedKey'
'500':
description: Internal server error (auth management client unavailable)
'502':
description: Failed to reach the shared auth service
/api/peers/attestation:
get:
summary: Read the auth service's public signing key statement
tags: ["security"]
description: >
The auth service's RSA public key, its kid, and the sha256 fingerprint
peers should pin after first contact (TOFU, D3).
responses:
'200':
description: Auth public key attestation
content:
application/json:
schema:
$ref: '#/components/schemas/Attestation'
'502':
description: Failed to reach the shared auth service
/api/peers/approve:
post:
summary: Approve a peer's fingerprint in the auth service trust store
tags: ["security"]
description: >
Forwards the instance owner's approval for a service's identity
fingerprint. After this call the service's requests to /auth/* proceed
normally (enforced by the auth service itself).
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PeerAction'
responses:
'200':
description: Peer approved
'400':
description: Missing service or fingerprint
'502':
description: Failed to reach the shared auth service
/api/peers/revoke:
post:
summary: Revoke a peer's fingerprint (returns it to the no-op path)
tags: ["security"]
description: >
Forwards the instance owner's revocation. Calls from that fingerprint
become uniform 403 no-ops again immediately (the SSH authorized_keys
"delete the line" action).
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PeerAction'
responses:
'200':
description: Peer revoked
'400':
description: Missing service or fingerprint
'502':
description: Failed to reach the shared auth service
/api/peers/reject:
post:
summary: Reject a pending peer authorization request
tags: ["security"]
description: >
Denies a first-contact authorization request without authorizing it.
The auth service drops the request from the pending list and audits
the decision; the peer stays on the no-op path (a later contact simply
re-records a fresh pending request).
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PeerAction'
responses:
'200':
description: Pending request rejected
'400':
description: Missing service or fingerprint
'502':
description: Failed to reach the shared auth service