148 lines
6.9 KiB
JSON
148 lines
6.9 KiB
JSON
{
|
|
"openapi": "3.1.0",
|
|
"info": {
|
|
"title": "YoVision Brain Event Ingress",
|
|
"version": "1.0.0",
|
|
"description": "Internal single-event transport. The candidate is the frozen event v0.1 object with only the Bell-owned id omitted."
|
|
},
|
|
"paths": {
|
|
"/internal/v1/event-candidates": {
|
|
"post": {
|
|
"operationId": "ingestBrainEventCandidate",
|
|
"summary": "Validate and persist one Brain event candidate",
|
|
"parameters": [
|
|
{"$ref": "#/components/parameters/KeyId"},
|
|
{"$ref": "#/components/parameters/Timestamp"},
|
|
{"$ref": "#/components/parameters/Nonce"},
|
|
{"$ref": "#/components/parameters/Signature"}
|
|
],
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {"$ref": "#/components/schemas/IngressRequest"}
|
|
}
|
|
}
|
|
},
|
|
"responses": {
|
|
"200": {"description": "The same producer/source candidate was already stored", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IngressResponse"}}}},
|
|
"201": {"description": "Candidate accepted and stored", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IngressResponse"}}}},
|
|
"400": {"$ref": "#/components/responses/Error"},
|
|
"401": {"$ref": "#/components/responses/Error"},
|
|
"403": {"$ref": "#/components/responses/Error"},
|
|
"409": {"$ref": "#/components/responses/Error"},
|
|
"413": {"$ref": "#/components/responses/Error"},
|
|
"422": {"$ref": "#/components/responses/Error"},
|
|
"503": {"$ref": "#/components/responses/Error"}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"components": {
|
|
"parameters": {
|
|
"KeyId": {"name": "X-YoVision-Key-Id", "in": "header", "required": true, "schema": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"}},
|
|
"Timestamp": {"name": "X-YoVision-Timestamp", "in": "header", "required": true, "schema": {"type": "string", "pattern": "^[0-9]{10,}$"}},
|
|
"Nonce": {"name": "X-YoVision-Nonce", "in": "header", "required": true, "schema": {"type": "string", "minLength": 22, "maxLength": 64}},
|
|
"Signature": {"name": "X-YoVision-Signature", "in": "header", "required": true, "schema": {"type": "string", "minLength": 43, "maxLength": 43}}
|
|
},
|
|
"schemas": {
|
|
"IngressRequest": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["schema_version", "producer_id", "candidate"],
|
|
"properties": {
|
|
"schema_version": {"const": 1},
|
|
"producer_id": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"},
|
|
"candidate": {"$ref": "#/components/schemas/EventV01Candidate"}
|
|
}
|
|
},
|
|
"EventV01Candidate": {
|
|
"description": "Exact docs/raw/contracts/event-v0.1.schema.json event object with the required Bell-owned id field removed; nested shapes and semantic assertions remain authoritative in that frozen contract.",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": [
|
|
"schema_version", "source_event_id", "tenant_id", "site_id", "device_id", "sensors",
|
|
"kind", "severity", "confidence", "occurred_at", "detected_at", "latency_seconds",
|
|
"config_version", "rule", "subject", "observation", "evidence", "dedup_key",
|
|
"aggregated_into", "outcome", "outcome_source", "outcome_reason", "diagnostics", "ext"
|
|
],
|
|
"properties": {
|
|
"schema_version": {"const": "0.1"},
|
|
"source_event_id": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,128}$"},
|
|
"tenant_id": {"type": "integer", "minimum": 1},
|
|
"site_id": {"type": "integer", "minimum": 1},
|
|
"device_id": {"type": "integer", "minimum": 1},
|
|
"sensors": {"type": "array", "minItems": 1, "items": {"type": "object"}},
|
|
"kind": {"type": "string", "pattern": "^[a-z][a-z0-9_]{2,63}$"},
|
|
"severity": {"type": "string", "enum": ["low", "medium", "high", "critical"]},
|
|
"confidence": {"type": ["number", "null"], "minimum": 0, "maximum": 1},
|
|
"occurred_at": {"type": "string", "format": "date-time"},
|
|
"detected_at": {"type": "string", "format": "date-time"},
|
|
"latency_seconds": {"type": "number", "minimum": 0},
|
|
"config_version": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
"rule": {"type": ["object", "null"]},
|
|
"subject": {"type": "object"},
|
|
"observation": {"type": ["object", "null"]},
|
|
"evidence": {"type": "object"},
|
|
"dedup_key": {"type": ["string", "null"]},
|
|
"aggregated_into": {"type": ["string", "null"]},
|
|
"outcome": {"type": "string"},
|
|
"outcome_source": {"type": ["string", "null"]},
|
|
"outcome_reason": {"type": ["string", "null"]},
|
|
"diagnostics": {"type": ["object", "null"]},
|
|
"ext": {"type": "object"}
|
|
}
|
|
},
|
|
"IngressResponse": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["schema_version", "producer_id", "source_event_id", "event_id", "status"],
|
|
"properties": {
|
|
"schema_version": {"const": 1},
|
|
"producer_id": {"type": "string"},
|
|
"source_event_id": {"type": "string"},
|
|
"event_id": {"type": "string", "pattern": "^evt_[0-9A-HJKMNP-TV-Z]{26}$"},
|
|
"status": {"type": "string", "enum": ["accepted", "duplicate"]}
|
|
}
|
|
},
|
|
"Error": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["error"],
|
|
"properties": {
|
|
"error": {"type": "string"},
|
|
"detail_code": {"type": "string"}
|
|
}
|
|
}
|
|
},
|
|
"responses": {
|
|
"Error": {"description": "Stable machine-readable failure", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}}
|
|
}
|
|
},
|
|
"x-yovision-final-event-schema": "docs/raw/contracts/event-v0.1.schema.json",
|
|
"x-yovision-signature": {
|
|
"algorithm": "HMAC-SHA256",
|
|
"canonical_lines": ["method", "escaped_path", "unix_timestamp_seconds", "base64url_nonce", "lowercase_hex_sha256_body"],
|
|
"clock_skew_seconds": 300,
|
|
"nonce_receipt_ttl_seconds": 600,
|
|
"minimum_secret_bytes": 32,
|
|
"key_document_shape": {"version": 1, "keys": [{"key_id": "brain-a", "producer_id": "brain-main", "secret_base64url": "external-secret"}]}
|
|
},
|
|
"x-yovision-delivery": {
|
|
"maximum_body_bytes": 1048576,
|
|
"deadline_seconds": 10,
|
|
"initial_retry_seconds": 1,
|
|
"maximum_retry_seconds": 300,
|
|
"maximum_attempts": 100,
|
|
"maximum_queued": 10000,
|
|
"success_statuses": ["accepted", "duplicate"],
|
|
"retryable_http": [401, 500, 502, 503, 504]
|
|
},
|
|
"x-yovision-idempotency": {
|
|
"source_scope": ["producer_id", "source_event_id"],
|
|
"same_candidate": "return duplicate with the original Bell event_id",
|
|
"different_candidate": "409 source_event_conflict",
|
|
"bell_owns_event_id": true
|
|
}
|
|
}
|