feat(portal): harden pilot v1 role contracts

This commit is contained in:
igor04091968
2026-06-06 12:54:00 +03:00
parent 9c57d6d2ce
commit 067ad0939c
24 changed files with 1991 additions and 208 deletions
@@ -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
View File
@@ -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>;