feat(portal): harden pilot v1 role contracts
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "AWatch-rus DetMir Portal API",
|
||||
"version": "2026-06-05.v1",
|
||||
"description": "Stable additive API contract for the current HTML portal and future React/Tauri clients. Clients must ignore unknown fields and tolerate missing optional fields."
|
||||
"version": "2026-06-06.pilot-v1",
|
||||
"description": "Stable additive API contract for the Rust server-rendered HTML portal and HTMX-compatible role slices. Dioxus, React, Tauri and Electron are not part of this Pilot v1 contract. Clients must ignore unknown fields and tolerate missing optional fields."
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
@@ -21,6 +21,24 @@
|
||||
{
|
||||
"name": "reports"
|
||||
},
|
||||
{
|
||||
"name": "roles"
|
||||
},
|
||||
{
|
||||
"name": "workforce"
|
||||
},
|
||||
{
|
||||
"name": "security"
|
||||
},
|
||||
{
|
||||
"name": "forensics"
|
||||
},
|
||||
{
|
||||
"name": "ueba"
|
||||
},
|
||||
{
|
||||
"name": "pfsense"
|
||||
},
|
||||
{
|
||||
"name": "incidents"
|
||||
},
|
||||
@@ -191,6 +209,15 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": "Return anonymized values when supported"
|
||||
},
|
||||
{
|
||||
"name": "role",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PortalRole"
|
||||
},
|
||||
"description": "Role slice. The X-AWatch-Role header is also accepted and is preferred by the portal UI."
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -207,6 +234,144 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/executive": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"roles"
|
||||
],
|
||||
"summary": "Executive role slice",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Executive dashboard and Workforce summary without security detail",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ReportPayload"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Role is not allowed to access executive scope"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workforce": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"workforce"
|
||||
],
|
||||
"summary": "Workforce role slice",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Workforce analytics, department comparison, owners, trends and markdown report",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ReportPayload"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Role is not allowed to access workforce scope"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/security": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"security"
|
||||
],
|
||||
"summary": "Security role slice",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Security risks, events, candidates and UEBA score without Workforce dashboard",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ReportPayload"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Role is not allowed to access security scope"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/forensics": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"forensics"
|
||||
],
|
||||
"summary": "Forensics role slice",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Investigations, timeline contract and export links",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ReportPayload"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Role is not allowed to access forensics scope"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ueba": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"ueba"
|
||||
],
|
||||
"summary": "UEBA score v1 rule-based contract",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Transparent rule-based UEBA score",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UebaPayload"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Role is not allowed to access UEBA scope"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/pfsense": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"pfsense"
|
||||
],
|
||||
"summary": "pfSense integration readiness contract",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Contract-only pfSense readiness payload with demo fixtures",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PfsenseReadiness"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Role is not allowed to access pfSense scope"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workforce/policy/explain": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -605,6 +770,45 @@
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"PortalRole": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"executive",
|
||||
"manager",
|
||||
"security",
|
||||
"forensics",
|
||||
"admin"
|
||||
]
|
||||
},
|
||||
"RoleContext": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"role",
|
||||
"scope",
|
||||
"server_enforced"
|
||||
],
|
||||
"properties": {
|
||||
"role": {
|
||||
"$ref": "#/components/schemas/PortalRole"
|
||||
},
|
||||
"role_label": {
|
||||
"type": "string"
|
||||
},
|
||||
"scope": {
|
||||
"type": "string"
|
||||
},
|
||||
"allowed_scopes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"server_enforced": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"JsonObject": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
@@ -713,6 +917,9 @@
|
||||
"$ref": "#/components/schemas/JsonObject"
|
||||
}
|
||||
},
|
||||
"role_context": {
|
||||
"$ref": "#/components/schemas/RoleContext"
|
||||
},
|
||||
"cases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -722,6 +929,156 @@
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"UebaPayload": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ok",
|
||||
"score",
|
||||
"severity",
|
||||
"score_components",
|
||||
"reason_codes",
|
||||
"model"
|
||||
],
|
||||
"properties": {
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"role_context": {
|
||||
"$ref": "#/components/schemas/RoleContext"
|
||||
},
|
||||
"score": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"normal",
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"critical"
|
||||
]
|
||||
},
|
||||
"score_components": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"activity_anomaly",
|
||||
"time_anomaly",
|
||||
"application_anomaly",
|
||||
"network_anomaly",
|
||||
"history_anomaly"
|
||||
],
|
||||
"properties": {
|
||||
"activity_anomaly": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
},
|
||||
"time_anomaly": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
},
|
||||
"application_anomaly": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
},
|
||||
"network_anomaly": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
},
|
||||
"history_anomaly": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"reason_codes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"explanation": {
|
||||
"type": "string"
|
||||
},
|
||||
"model": {
|
||||
"$ref": "#/components/schemas/JsonObject"
|
||||
},
|
||||
"risk": {
|
||||
"$ref": "#/components/schemas/JsonObject"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"PfsenseReadiness": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ok",
|
||||
"contract_version",
|
||||
"status",
|
||||
"ingestion_available",
|
||||
"firewall_events",
|
||||
"vpn_events",
|
||||
"traffic_summary",
|
||||
"top_destinations"
|
||||
],
|
||||
"properties": {
|
||||
"ok": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"role_context": {
|
||||
"$ref": "#/components/schemas/RoleContext"
|
||||
},
|
||||
"contract_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"contract_only",
|
||||
"available"
|
||||
]
|
||||
},
|
||||
"siem": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"ingestion_available": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"firewall_events": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/JsonObject"
|
||||
}
|
||||
},
|
||||
"vpn_events": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/JsonObject"
|
||||
}
|
||||
},
|
||||
"traffic_summary": {
|
||||
"$ref": "#/components/schemas/JsonObject"
|
||||
},
|
||||
"top_destinations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/JsonObject"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
},
|
||||
"IncidentReviewRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
+71
-1
@@ -1,5 +1,6 @@
|
||||
export type ISODateTime = string;
|
||||
|
||||
export type PortalRole = "executive" | "manager" | "security" | "forensics" | "admin";
|
||||
export type RiskLevel = "LOW" | "MEDIUM" | "HIGH" | "CRITICAL" | "UNKNOWN";
|
||||
export type ReviewStatus =
|
||||
| "NEW"
|
||||
@@ -36,6 +37,15 @@ export interface ContractIndex {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface RoleContext {
|
||||
role: PortalRole;
|
||||
role_label?: string;
|
||||
scope: string;
|
||||
allowed_scopes?: string[];
|
||||
server_enforced: boolean;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ExecutiveDashboard {
|
||||
trust_kpi_score?: number;
|
||||
agent_coverage_pct?: number;
|
||||
@@ -154,6 +164,7 @@ export interface CaseStatusRequest {
|
||||
|
||||
export interface ReportsResponse {
|
||||
ok: boolean;
|
||||
role_context?: RoleContext;
|
||||
generated_at_utc?: ISODateTime;
|
||||
executive_points?: string[];
|
||||
executive_dashboard?: ExecutiveDashboard;
|
||||
@@ -166,6 +177,59 @@ export interface ReportsResponse {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface UebaResponse {
|
||||
ok: boolean;
|
||||
role_context?: RoleContext;
|
||||
score: number | null;
|
||||
severity: "normal" | "low" | "medium" | "high" | "critical" | string;
|
||||
status?: string;
|
||||
score_components: {
|
||||
activity_anomaly: number;
|
||||
time_anomaly: number;
|
||||
application_anomaly: number;
|
||||
network_anomaly: number;
|
||||
history_anomaly: number;
|
||||
};
|
||||
reason_codes: string[];
|
||||
explanation: string;
|
||||
model: JsonObject;
|
||||
risk: JsonObject;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface PfsenseFirewallEvent {
|
||||
timestamp: ISODateTime;
|
||||
source_host: string;
|
||||
destination: string;
|
||||
action: string;
|
||||
rule_id?: string;
|
||||
protocol?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface PfsenseVpnEvent {
|
||||
timestamp: ISODateTime;
|
||||
source_host: string;
|
||||
user_ref?: string;
|
||||
action: string;
|
||||
tunnel?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface PfsenseReadinessResponse {
|
||||
ok: boolean;
|
||||
role_context?: RoleContext;
|
||||
contract_version: string;
|
||||
status: "contract_only" | "available" | string;
|
||||
siem: boolean;
|
||||
ingestion_available: boolean;
|
||||
firewall_events: PfsenseFirewallEvent[];
|
||||
vpn_events: PfsenseVpnEvent[];
|
||||
traffic_summary: JsonObject;
|
||||
top_destinations: JsonObject[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface CaseListResponse {
|
||||
ok: boolean;
|
||||
cases: CaseItem[];
|
||||
@@ -178,7 +242,13 @@ export interface DetMirPortalApi {
|
||||
getOperator(): Promise<JsonObject>;
|
||||
getManager(): Promise<JsonObject>;
|
||||
getOwner(): Promise<JsonObject>;
|
||||
getReports(options?: { anonymize?: boolean }): Promise<ReportsResponse>;
|
||||
getReports(options?: { anonymize?: boolean; role?: PortalRole }): Promise<ReportsResponse>;
|
||||
getExecutive(options?: { role?: PortalRole }): Promise<ReportsResponse>;
|
||||
getWorkforce(options?: { role?: PortalRole }): Promise<ReportsResponse>;
|
||||
getSecurity(options?: { role?: PortalRole }): Promise<ReportsResponse>;
|
||||
getForensics(options?: { role?: PortalRole }): Promise<ReportsResponse>;
|
||||
getUeba(options?: { role?: PortalRole }): Promise<UebaResponse>;
|
||||
getPfsense(options?: { role?: PortalRole }): Promise<PfsenseReadinessResponse>;
|
||||
getIncidents(): Promise<JsonObject>;
|
||||
getCases(): Promise<CaseListResponse>;
|
||||
createCase(request: CreateCaseRequest): Promise<JsonObject>;
|
||||
|
||||
Reference in New Issue
Block a user