From ff76751629f3e2cf2f7af1753bc3119388941cf4 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Tue, 24 Mar 2026 08:48:12 +0100 Subject: [PATCH] =?UTF-8?q?refactor:=20rename=20agent=20group=E2=86=92appl?= =?UTF-8?q?ication=20across=20entire=20codebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete the group→application terminology rename in the agent registry subsystem: - AgentInfo: field group → application, all wither methods updated - AgentRegistryService: findByGroup → findByApplication - AgentInstanceResponse: field group → application (API response) - AgentRegistrationRequest: field group → application (API request) - JwtServiceImpl: parameter names group → application (JWT claim string "group" preserved for token backward compatibility) - All controllers, lifecycle monitor, command controller updated - Integration tests: JSON request bodies "group" → "application" - Frontend: schema.d.ts, openapi.json, agent queries, AgentHealth RBAC group references (groups table, GroupAdminController, etc.) are NOT affected — they are a separate domain concept. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../app/agent/AgentLifecycleMonitor.java | 2 +- .../controller/AgentCommandController.java | 2 +- .../AgentRegistrationController.java | 34 +- .../controller/DiagramRenderController.java | 12 +- .../app/controller/ExecutionController.java | 2 +- .../controller/RouteCatalogController.java | 4 +- .../app/controller/SearchController.java | 2 +- .../server/app/dto/AgentInstanceResponse.java | 6 +- .../app/dto/AgentRegistrationRequest.java | 2 +- .../server/app/security/JwtServiceImpl.java | 16 +- .../server/app/TestSecurityHelper.java | 4 +- .../controller/AgentCommandControllerIT.java | 6 +- .../AgentRegistrationControllerIT.java | 2 +- .../app/controller/AgentSseControllerIT.java | 6 +- .../server/app/security/BootstrapTokenIT.java | 4 +- .../server/app/security/JwtRefreshIT.java | 2 +- .../server/app/security/JwtServiceTest.java | 4 +- .../app/security/RegistrationSecurityIT.java | 2 +- .../server/app/security/SseSigningIT.java | 2 +- .../server/core/agent/AgentInfo.java | 16 +- .../core/agent/AgentRegistryService.java | 14 +- .../server/core/security/JwtService.java | 24 +- .../core/agent/AgentRegistryServiceTest.java | 4 +- ui/src/api/openapi.json | 4516 +---------------- ui/src/api/queries/agents.ts | 6 +- ui/src/api/queries/diagrams.ts | 2 +- ui/src/api/schema.d.ts | 20 +- ui/src/pages/AgentHealth/AgentHealth.tsx | 6 +- 28 files changed, 104 insertions(+), 4618 deletions(-) diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/AgentLifecycleMonitor.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/AgentLifecycleMonitor.java index ed5dda7b..e77d1793 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/AgentLifecycleMonitor.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/agent/AgentLifecycleMonitor.java @@ -51,7 +51,7 @@ public class AgentLifecycleMonitor { if (before != null && before != agent.state()) { String eventType = mapTransitionEvent(before, agent.state()); if (eventType != null) { - agentEventService.recordEvent(agent.id(), agent.group(), eventType, + agentEventService.recordEvent(agent.id(), agent.application(), eventType, agent.name() + " " + before + " -> " + agent.state()); } } diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java index 9df78b7e..9d34cb7d 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentCommandController.java @@ -92,7 +92,7 @@ public class AgentCommandController { List agents = registryService.findAll().stream() .filter(a -> a.state() == AgentState.LIVE) - .filter(a -> group.equals(a.group())) + .filter(a -> group.equals(a.application())) .toList(); List commandIds = new ArrayList<>(); diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java index 4ad896e0..a04f294d 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/AgentRegistrationController.java @@ -102,21 +102,21 @@ public class AgentRegistrationController { return ResponseEntity.badRequest().build(); } - String group = request.group() != null ? request.group() : "default"; + String application = request.application() != null ? request.application() : "default"; List routeIds = request.routeIds() != null ? request.routeIds() : List.of(); var capabilities = request.capabilities() != null ? request.capabilities() : Collections.emptyMap(); AgentInfo agent = registryService.register( - request.agentId(), request.name(), group, request.version(), routeIds, capabilities); - log.info("Agent registered: {} (name={}, group={})", request.agentId(), request.name(), group); + request.agentId(), request.name(), application, request.version(), routeIds, capabilities); + log.info("Agent registered: {} (name={}, application={})", request.agentId(), request.name(), application); - agentEventService.recordEvent(request.agentId(), group, "REGISTERED", + agentEventService.recordEvent(request.agentId(), application, "REGISTERED", "Agent registered: " + request.name()); // Issue JWT tokens with AGENT role List roles = List.of("AGENT"); - String accessToken = jwtService.createAccessToken(request.agentId(), group, roles); - String refreshToken = jwtService.createRefreshToken(request.agentId(), group, roles); + String accessToken = jwtService.createAccessToken(request.agentId(), application, roles); + String refreshToken = jwtService.createRefreshToken(request.agentId(), application, roles); return ResponseEntity.ok(new AgentRegistrationResponse( agent.id(), @@ -166,8 +166,8 @@ public class AgentRegistrationController { // Preserve roles from refresh token List roles = result.roles().isEmpty() ? List.of("AGENT") : result.roles(); - String newAccessToken = jwtService.createAccessToken(agentId, agent.group(), roles); - String newRefreshToken = jwtService.createRefreshToken(agentId, agent.group(), roles); + String newAccessToken = jwtService.createAccessToken(agentId, agent.application(), roles); + String newRefreshToken = jwtService.createRefreshToken(agentId, agent.application(), roles); return ResponseEntity.ok(new AgentRefreshResponse(newAccessToken, newRefreshToken)); } @@ -187,13 +187,13 @@ public class AgentRegistrationController { @GetMapping @Operation(summary = "List all agents", - description = "Returns all registered agents with runtime metrics, optionally filtered by status and/or group") + description = "Returns all registered agents with runtime metrics, optionally filtered by status and/or application") @ApiResponse(responseCode = "200", description = "Agent list returned") @ApiResponse(responseCode = "400", description = "Invalid status filter", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) public ResponseEntity> listAgents( @RequestParam(required = false) String status, - @RequestParam(required = false) String group) { + @RequestParam(required = false) String application) { List agents; if (status != null) { @@ -207,10 +207,10 @@ public class AgentRegistrationController { agents = registryService.findAll(); } - // Apply group filter if specified - if (group != null && !group.isBlank()) { + // Apply application filter if specified + if (application != null && !application.isBlank()) { agents = agents.stream() - .filter(a -> group.equals(a.group())) + .filter(a -> application.equals(a.application())) .toList(); } @@ -221,11 +221,11 @@ public class AgentRegistrationController { List response = finalAgents.stream() .map(a -> { AgentInstanceResponse dto = AgentInstanceResponse.from(a); - double[] m = agentMetrics.get(a.group()); + double[] m = agentMetrics.get(a.application()); if (m != null) { - long groupAgentCount = finalAgents.stream() - .filter(ag -> ag.group().equals(a.group())).count(); - double agentTps = groupAgentCount > 0 ? m[0] / groupAgentCount : 0; + long appAgentCount = finalAgents.stream() + .filter(ag -> ag.application().equals(a.application())).count(); + double agentTps = appAgentCount > 0 ? m[0] / appAgentCount : 0; double errorRate = m[1]; int activeRoutes = (int) m[2]; return dto.withMetrics(agentTps, errorRate, activeRoutes); diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramRenderController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramRenderController.java index d8f722e7..41aeda19 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramRenderController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/DiagramRenderController.java @@ -90,14 +90,14 @@ public class DiagramRenderController { } @GetMapping - @Operation(summary = "Find diagram by application group and route ID", - description = "Resolves group to agent IDs and finds the latest diagram for the route") + @Operation(summary = "Find diagram by application and route ID", + description = "Resolves application to agent IDs and finds the latest diagram for the route") @ApiResponse(responseCode = "200", description = "Diagram layout returned") - @ApiResponse(responseCode = "404", description = "No diagram found for the given group and route") - public ResponseEntity findByGroupAndRoute( - @RequestParam String group, + @ApiResponse(responseCode = "404", description = "No diagram found for the given application and route") + public ResponseEntity findByApplicationAndRoute( + @RequestParam String application, @RequestParam String routeId) { - List agentIds = registryService.findByGroup(group).stream() + List agentIds = registryService.findByApplication(application).stream() .map(AgentInfo::id) .toList(); diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java index d056aeba..a37ea643 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/ExecutionController.java @@ -70,7 +70,7 @@ public class ExecutionController { private String resolveApplicationName(String agentId) { AgentInfo agent = registryService.findById(agentId); - return agent != null ? agent.group() : ""; + return agent != null ? agent.application() : ""; } private List parsePayload(String body) throws JsonProcessingException { diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RouteCatalogController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RouteCatalogController.java index d52df03a..b7fc07b2 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RouteCatalogController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/RouteCatalogController.java @@ -47,9 +47,9 @@ public class RouteCatalogController { public ResponseEntity> getCatalog() { List allAgents = registryService.findAll(); - // Group agents by application (group name) + // Group agents by application name Map> agentsByApp = allAgents.stream() - .collect(Collectors.groupingBy(AgentInfo::group, LinkedHashMap::new, Collectors.toList())); + .collect(Collectors.groupingBy(AgentInfo::application, LinkedHashMap::new, Collectors.toList())); // Collect all distinct routes per app Map> routesByApp = new LinkedHashMap<>(); diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/SearchController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/SearchController.java index b09e07ab..d1045086 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/SearchController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/controller/SearchController.java @@ -134,7 +134,7 @@ public class SearchController { if (application == null || application.isBlank()) { return null; } - return registryService.findByGroup(application).stream() + return registryService.findByApplication(application).stream() .map(AgentInfo::id) .toList(); } diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/dto/AgentInstanceResponse.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/dto/AgentInstanceResponse.java index e3232da5..15e5c731 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/dto/AgentInstanceResponse.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/dto/AgentInstanceResponse.java @@ -13,7 +13,7 @@ import java.util.Map; public record AgentInstanceResponse( @NotNull String id, @NotNull String name, - @NotNull String group, + @NotNull String application, @NotNull String status, @NotNull List routeIds, @NotNull Instant registeredAt, @@ -29,7 +29,7 @@ public record AgentInstanceResponse( public static AgentInstanceResponse from(AgentInfo info) { long uptime = Duration.between(info.registeredAt(), Instant.now()).toSeconds(); return new AgentInstanceResponse( - info.id(), info.name(), info.group(), + info.id(), info.name(), info.application(), info.state().name(), info.routeIds(), info.registeredAt(), info.lastHeartbeat(), info.version(), info.capabilities(), @@ -41,7 +41,7 @@ public record AgentInstanceResponse( public AgentInstanceResponse withMetrics(double tps, double errorRate, int activeRoutes) { return new AgentInstanceResponse( - id, name, group, status, routeIds, registeredAt, lastHeartbeat, + id, name, application, status, routeIds, registeredAt, lastHeartbeat, version, capabilities, tps, errorRate, activeRoutes, totalRoutes, uptimeSeconds ); diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/dto/AgentRegistrationRequest.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/dto/AgentRegistrationRequest.java index 27dee2b8..9ea0624b 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/dto/AgentRegistrationRequest.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/dto/AgentRegistrationRequest.java @@ -10,7 +10,7 @@ import java.util.Map; public record AgentRegistrationRequest( @NotNull String agentId, @NotNull String name, - @Schema(defaultValue = "default") String group, + @Schema(defaultValue = "default") String application, String version, List routeIds, Map capabilities diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtServiceImpl.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtServiceImpl.java index 40cb29a6..b04a16f3 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtServiceImpl.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtServiceImpl.java @@ -60,13 +60,13 @@ public class JwtServiceImpl implements JwtService { } @Override - public String createAccessToken(String subject, String group, List roles) { - return createToken(subject, group, roles, "access", properties.getAccessTokenExpiryMs()); + public String createAccessToken(String subject, String application, List roles) { + return createToken(subject, application, roles, "access", properties.getAccessTokenExpiryMs()); } @Override - public String createRefreshToken(String subject, String group, List roles) { - return createToken(subject, group, roles, "refresh", properties.getRefreshTokenExpiryMs()); + public String createRefreshToken(String subject, String application, List roles) { + return createToken(subject, application, roles, "refresh", properties.getRefreshTokenExpiryMs()); } @Override @@ -84,12 +84,12 @@ public class JwtServiceImpl implements JwtService { return validateAccessToken(token).subject(); } - private String createToken(String subject, String group, List roles, + private String createToken(String subject, String application, List roles, String type, long expiryMs) { Instant now = Instant.now(); JWTClaimsSet claims = new JWTClaimsSet.Builder() .subject(subject) - .claim("group", group) + .claim("group", application) .claim("type", type) .claim("roles", roles) .issueTime(Date.from(now)) @@ -132,7 +132,7 @@ public class JwtServiceImpl implements JwtService { throw new InvalidTokenException("Token has no subject"); } - String group = claims.getStringClaim("group"); + String application = claims.getStringClaim("group"); // Extract roles — may be absent in legacy tokens List roles; @@ -145,7 +145,7 @@ public class JwtServiceImpl implements JwtService { roles = List.of(); } - return new JwtValidationResult(subject, group, roles); + return new JwtValidationResult(subject, application, roles); } catch (ParseException e) { throw new InvalidTokenException("Failed to parse JWT", e); } catch (JOSEException e) { diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/TestSecurityHelper.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/TestSecurityHelper.java index bafe8d0a..b261aef0 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/TestSecurityHelper.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/TestSecurityHelper.java @@ -37,8 +37,8 @@ public class TestSecurityHelper { /** * Returns a valid JWT access token with the given roles (no agent registration). */ - public String createToken(String subject, String group, List roles) { - return jwtService.createAccessToken(subject, group, roles); + public String createToken(String subject, String application, List roles) { + return jwtService.createAccessToken(subject, application, roles); } /** diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java index b6d791d7..412e443b 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentCommandControllerIT.java @@ -38,17 +38,17 @@ class AgentCommandControllerIT extends AbstractPostgresIT { operatorJwt = securityHelper.operatorToken(); } - private ResponseEntity registerAgent(String agentId, String name, String group) { + private ResponseEntity registerAgent(String agentId, String name, String application) { String json = """ { "agentId": "%s", "name": "%s", - "group": "%s", + "application": "%s", "version": "1.0.0", "routeIds": ["route-1"], "capabilities": {} } - """.formatted(agentId, name, group); + """.formatted(agentId, name, application); return restTemplate.postForEntity( "/api/v1/agents/register", diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java index 12cbf02e..4167f325 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentRegistrationControllerIT.java @@ -41,7 +41,7 @@ class AgentRegistrationControllerIT extends AbstractPostgresIT { { "agentId": "%s", "name": "%s", - "group": "test-group", + "application": "test-group", "version": "1.0.0", "routeIds": ["route-1", "route-2"], "capabilities": {"tracing": true} diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java index 78a3743f..751709a5 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/controller/AgentSseControllerIT.java @@ -53,17 +53,17 @@ class AgentSseControllerIT extends AbstractPostgresIT { operatorJwt = securityHelper.operatorToken(); } - private ResponseEntity registerAgent(String agentId, String name, String group) { + private ResponseEntity registerAgent(String agentId, String name, String application) { String json = """ { "agentId": "%s", "name": "%s", - "group": "%s", + "application": "%s", "version": "1.0.0", "routeIds": ["route-1"], "capabilities": {} } - """.formatted(agentId, name, group); + """.formatted(agentId, name, application); return restTemplate.postForEntity( "/api/v1/agents/register", diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/BootstrapTokenIT.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/BootstrapTokenIT.java index 3ce87894..61ad7d61 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/BootstrapTokenIT.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/BootstrapTokenIT.java @@ -29,7 +29,7 @@ class BootstrapTokenIT extends AbstractPostgresIT { { "agentId": "bootstrap-test-agent", "name": "Bootstrap Test", - "group": "test-group", + "application": "test-group", "version": "1.0.0", "routeIds": [], "capabilities": {} @@ -97,7 +97,7 @@ class BootstrapTokenIT extends AbstractPostgresIT { { "agentId": "bootstrap-test-previous", "name": "Previous Token Test", - "group": "test-group", + "application": "test-group", "version": "1.0.0", "routeIds": [], "capabilities": {} diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtRefreshIT.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtRefreshIT.java index ba46c416..f5b62817 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtRefreshIT.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtRefreshIT.java @@ -39,7 +39,7 @@ class JwtRefreshIT extends AbstractPostgresIT { { "agentId": "%s", "name": "Refresh Test Agent", - "group": "test-group", + "application": "test-group", "version": "1.0.0", "routeIds": [], "capabilities": {} diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtServiceTest.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtServiceTest.java index 3219a442..5c4d0f71 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtServiceTest.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/JwtServiceTest.java @@ -78,7 +78,7 @@ class JwtServiceTest { String token = jwtService.createAccessToken("user:admin", "user", roles); JwtService.JwtValidationResult result = jwtService.validateAccessToken(token); assertEquals("user:admin", result.subject()); - assertEquals("user", result.group()); + assertEquals("user", result.application()); assertEquals(roles, result.roles()); } @@ -88,7 +88,7 @@ class JwtServiceTest { String token = jwtService.createRefreshToken("agent-1", "default", roles); JwtService.JwtValidationResult result = jwtService.validateRefreshToken(token); assertEquals("agent-1", result.subject()); - assertEquals("default", result.group()); + assertEquals("default", result.application()); assertEquals(roles, result.roles()); } diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/RegistrationSecurityIT.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/RegistrationSecurityIT.java index 54c17e71..64fea8e6 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/RegistrationSecurityIT.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/RegistrationSecurityIT.java @@ -32,7 +32,7 @@ class RegistrationSecurityIT extends AbstractPostgresIT { { "agentId": "%s", "name": "Security Test Agent", - "group": "test-group", + "application": "test-group", "version": "1.0.0", "routeIds": [], "capabilities": {} diff --git a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SseSigningIT.java b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SseSigningIT.java index 11e0ed6b..fd54a2ba 100644 --- a/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SseSigningIT.java +++ b/cameleer3-server-app/src/test/java/com/cameleer3/server/app/security/SseSigningIT.java @@ -90,7 +90,7 @@ class SseSigningIT extends AbstractPostgresIT { { "agentId": "%s", "name": "SSE Signing Test Agent", - "group": "test-group", + "application": "test-group", "version": "1.0.0", "routeIds": ["route-1"], "capabilities": {} diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentInfo.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentInfo.java index d8a883dd..ade2d765 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentInfo.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentInfo.java @@ -13,7 +13,7 @@ import java.util.Map; * * @param id agent-provided persistent identifier * @param name human-readable agent name - * @param group logical grouping (e.g., "order-service-prod") + * @param application application name (e.g., "order-service-prod") * @param version agent software version * @param routeIds list of Camel route IDs managed by this agent * @param capabilities agent-declared capabilities (free-form) @@ -25,7 +25,7 @@ import java.util.Map; public record AgentInfo( String id, String name, - String group, + String application, String version, List routeIds, Map capabilities, @@ -36,28 +36,28 @@ public record AgentInfo( ) { public AgentInfo withState(AgentState newState) { - return new AgentInfo(id, name, group, version, routeIds, capabilities, + return new AgentInfo(id, name, application, version, routeIds, capabilities, newState, registeredAt, lastHeartbeat, staleTransitionTime); } public AgentInfo withLastHeartbeat(Instant newLastHeartbeat) { - return new AgentInfo(id, name, group, version, routeIds, capabilities, + return new AgentInfo(id, name, application, version, routeIds, capabilities, state, registeredAt, newLastHeartbeat, staleTransitionTime); } public AgentInfo withRegisteredAt(Instant newRegisteredAt) { - return new AgentInfo(id, name, group, version, routeIds, capabilities, + return new AgentInfo(id, name, application, version, routeIds, capabilities, state, newRegisteredAt, lastHeartbeat, staleTransitionTime); } public AgentInfo withStaleTransitionTime(Instant newStaleTransitionTime) { - return new AgentInfo(id, name, group, version, routeIds, capabilities, + return new AgentInfo(id, name, application, version, routeIds, capabilities, state, registeredAt, lastHeartbeat, newStaleTransitionTime); } - public AgentInfo withMetadata(String name, String group, String version, + public AgentInfo withMetadata(String name, String application, String version, List routeIds, Map capabilities) { - return new AgentInfo(id, name, group, version, routeIds, capabilities, + return new AgentInfo(id, name, application, version, routeIds, capabilities, state, registeredAt, lastHeartbeat, staleTransitionTime); } } diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java index bfb72e0b..a3b50692 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/agent/AgentRegistryService.java @@ -43,10 +43,10 @@ public class AgentRegistryService { * Register a new agent or re-register an existing one. * Re-registration updates metadata, transitions state to LIVE, and resets timestamps. */ - public AgentInfo register(String id, String name, String group, String version, + public AgentInfo register(String id, String name, String application, String version, List routeIds, Map capabilities) { Instant now = Instant.now(); - AgentInfo newAgent = new AgentInfo(id, name, group, version, + AgentInfo newAgent = new AgentInfo(id, name, application, version, List.copyOf(routeIds), Map.copyOf(capabilities), AgentState.LIVE, now, now, null); @@ -55,13 +55,13 @@ public class AgentRegistryService { // Re-registration: update metadata, reset to LIVE log.info("Agent {} re-registering (was {})", id, existing.state()); return existing - .withMetadata(name, group, version, List.copyOf(routeIds), Map.copyOf(capabilities)) + .withMetadata(name, application, version, List.copyOf(routeIds), Map.copyOf(capabilities)) .withState(AgentState.LIVE) .withLastHeartbeat(now) .withRegisteredAt(now) .withStaleTransitionTime(null); } - log.info("Agent {} registered (name={}, group={})", id, name, group); + log.info("Agent {} registered (name={}, application={})", id, name, application); return newAgent; }); @@ -168,11 +168,11 @@ public class AgentRegistryService { } /** - * Return all agents belonging to the given application group. + * Return all agents belonging to the given application. */ - public List findByGroup(String group) { + public List findByApplication(String application) { return agents.values().stream() - .filter(a -> group.equals(a.group())) + .filter(a -> application.equals(a.application())) .collect(Collectors.toList()); } diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/JwtService.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/JwtService.java index f3a31f78..a10b617f 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/JwtService.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/security/JwtService.java @@ -14,21 +14,21 @@ public interface JwtService { /** * Validated JWT payload. * - * @param subject the {@code sub} claim (agent ID or {@code user:}) - * @param group the {@code group} claim - * @param roles the {@code roles} claim (e.g. {@code ["AGENT"]}, {@code ["ADMIN"]}) + * @param subject the {@code sub} claim (agent ID or {@code user:}) + * @param application the {@code group} claim (application name) + * @param roles the {@code roles} claim (e.g. {@code ["AGENT"]}, {@code ["ADMIN"]}) */ - record JwtValidationResult(String subject, String group, List roles) {} + record JwtValidationResult(String subject, String application, List roles) {} /** - * Creates a signed access JWT with the given subject, group, and roles. + * Creates a signed access JWT with the given subject, application, and roles. */ - String createAccessToken(String subject, String group, List roles); + String createAccessToken(String subject, String application, List roles); /** - * Creates a signed refresh JWT with the given subject, group, and roles. + * Creates a signed refresh JWT with the given subject, application, and roles. */ - String createRefreshToken(String subject, String group, List roles); + String createRefreshToken(String subject, String application, List roles); /** * Validates an access token and returns the full validation result. @@ -46,12 +46,12 @@ public interface JwtService { // --- Backward-compatible defaults (delegate to role-aware methods) --- - default String createAccessToken(String subject, String group) { - return createAccessToken(subject, group, List.of()); + default String createAccessToken(String subject, String application) { + return createAccessToken(subject, application, List.of()); } - default String createRefreshToken(String subject, String group) { - return createRefreshToken(subject, group, List.of()); + default String createRefreshToken(String subject, String application) { + return createRefreshToken(subject, application, List.of()); } default String validateAndExtractAgentId(String token) { diff --git a/cameleer3-server-core/src/test/java/com/cameleer3/server/core/agent/AgentRegistryServiceTest.java b/cameleer3-server-core/src/test/java/com/cameleer3/server/core/agent/AgentRegistryServiceTest.java index 3f46edd0..b3506153 100644 --- a/cameleer3-server-core/src/test/java/com/cameleer3/server/core/agent/AgentRegistryServiceTest.java +++ b/cameleer3-server-core/src/test/java/com/cameleer3/server/core/agent/AgentRegistryServiceTest.java @@ -32,7 +32,7 @@ class AgentRegistryServiceTest { assertThat(agent).isNotNull(); assertThat(agent.id()).isEqualTo("agent-1"); assertThat(agent.name()).isEqualTo("Order Agent"); - assertThat(agent.group()).isEqualTo("order-svc"); + assertThat(agent.application()).isEqualTo("order-svc"); assertThat(agent.version()).isEqualTo("1.0.0"); assertThat(agent.routeIds()).containsExactly("route1", "route2"); assertThat(agent.capabilities()).containsEntry("feature", "tracing"); @@ -52,7 +52,7 @@ class AgentRegistryServiceTest { assertThat(updated.id()).isEqualTo("agent-1"); assertThat(updated.name()).isEqualTo("New Name"); - assertThat(updated.group()).isEqualTo("new-group"); + assertThat(updated.application()).isEqualTo("new-group"); assertThat(updated.version()).isEqualTo("2.0.0"); assertThat(updated.routeIds()).containsExactly("route1", "route2"); assertThat(updated.capabilities()).containsEntry("new", "cap"); diff --git a/ui/src/api/openapi.json b/ui/src/api/openapi.json index 91b1c054..26732fd9 100644 --- a/ui/src/api/openapi.json +++ b/ui/src/api/openapi.json @@ -1,4515 +1 @@ -{ - "openapi": "3.1.0", - "info": { - "title": "Cameleer3 Server API", - "version": "1.0" - }, - "servers": [], - "security": [ - { - "bearer": [] - } - ], - "tags": [ - { - "name": "Agent Events", - "description": "Agent lifecycle event log" - }, - { - "name": "Database Admin", - "description": "Database monitoring and management (ADMIN only)" - }, - { - "name": "Threshold Admin", - "description": "Monitoring threshold configuration (ADMIN only)" - }, - { - "name": "Agent Commands", - "description": "Command push endpoints for agent communication" - }, - { - "name": "User Admin", - "description": "User management (ADMIN only)" - }, - { - "name": "Agent Management", - "description": "Agent registration and lifecycle endpoints" - }, - { - "name": "Authentication", - "description": "Login and token refresh endpoints" - }, - { - "name": "Role Admin", - "description": "Role management (ADMIN only)" - }, - { - "name": "RBAC Stats", - "description": "RBAC statistics (ADMIN only)" - }, - { - "name": "OIDC Config Admin", - "description": "OIDC provider configuration (ADMIN only)" - }, - { - "name": "Route Metrics", - "description": "Route performance metrics" - }, - { - "name": "Search", - "description": "Transaction search endpoints" - }, - { - "name": "Agent SSE", - "description": "Server-Sent Events endpoint for agent communication" - }, - { - "name": "Ingestion", - "description": "Data ingestion endpoints" - }, - { - "name": "Audit Log", - "description": "Audit log viewer (ADMIN only)" - }, - { - "name": "Group Admin", - "description": "Group management (ADMIN only)" - }, - { - "name": "Diagrams", - "description": "Diagram rendering endpoints" - }, - { - "name": "OpenSearch Admin", - "description": "OpenSearch monitoring and management (ADMIN only)" - }, - { - "name": "Detail", - "description": "Execution detail and processor snapshot endpoints" - }, - { - "name": "Route Catalog", - "description": "Route catalog and discovery" - } - ], - "paths": { - "/admin/users/{userId}": { - "get": { - "tags": [ - "User Admin" - ], - "summary": "Get user by ID with RBAC detail", - "operationId": "getUser", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "User found", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/UserDetail" - } - } - } - }, - "404": { - "description": "User not found", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/UserDetail" - } - } - } - } - } - }, - "put": { - "tags": [ - "User Admin" - ], - "summary": "Update user display name or email", - "operationId": "updateUser", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateUserRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "User updated" - }, - "404": { - "description": "User not found" - } - } - }, - "delete": { - "tags": [ - "User Admin" - ], - "summary": "Delete user", - "operationId": "deleteUser", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "User deleted" - } - } - } - }, - "/admin/thresholds": { - "get": { - "tags": [ - "Threshold Admin" - ], - "summary": "Get current threshold configuration", - "operationId": "getThresholds", - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ThresholdConfig" - } - } - } - } - } - }, - "put": { - "tags": [ - "Threshold Admin" - ], - "summary": "Update threshold configuration", - "operationId": "updateThresholds", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ThresholdConfigRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ThresholdConfig" - } - } - } - } - } - } - }, - "/admin/roles/{id}": { - "get": { - "tags": [ - "Role Admin" - ], - "summary": "Get role by ID with effective principals", - "operationId": "getRole", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Role found", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/RoleDetail" - } - } - } - }, - "404": { - "description": "Role not found", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/RoleDetail" - } - } - } - } - } - }, - "put": { - "tags": [ - "Role Admin" - ], - "summary": "Update a custom role", - "operationId": "updateRole", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateRoleRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Role updated" - }, - "403": { - "description": "Cannot modify system role" - }, - "404": { - "description": "Role not found" - } - } - }, - "delete": { - "tags": [ - "Role Admin" - ], - "summary": "Delete a custom role", - "operationId": "deleteRole", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "204": { - "description": "Role deleted" - }, - "403": { - "description": "Cannot delete system role" - }, - "404": { - "description": "Role not found" - } - } - } - }, - "/admin/oidc": { - "get": { - "tags": [ - "OIDC Config Admin" - ], - "summary": "Get OIDC configuration", - "operationId": "getConfig", - "responses": { - "200": { - "description": "Current OIDC configuration (client_secret masked)", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/OidcAdminConfigResponse" - } - } - } - } - } - }, - "put": { - "tags": [ - "OIDC Config Admin" - ], - "summary": "Save OIDC configuration", - "operationId": "saveConfig", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/OidcAdminConfigRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Configuration saved", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/OidcAdminConfigResponse" - } - } - } - }, - "400": { - "description": "Invalid configuration", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - }, - "delete": { - "tags": [ - "OIDC Config Admin" - ], - "summary": "Delete OIDC configuration", - "operationId": "deleteConfig", - "responses": { - "204": { - "description": "Configuration deleted" - } - } - } - }, - "/admin/groups/{id}": { - "get": { - "tags": [ - "Group Admin" - ], - "summary": "Get group by ID with effective roles", - "operationId": "getGroup", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Group found", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/GroupDetail" - } - } - } - }, - "404": { - "description": "Group not found", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/GroupDetail" - } - } - } - } - } - }, - "put": { - "tags": [ - "Group Admin" - ], - "summary": "Update group name or parent", - "operationId": "updateGroup", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateGroupRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Group updated" - }, - "404": { - "description": "Group not found" - }, - "409": { - "description": "Cycle detected in group hierarchy" - } - } - }, - "delete": { - "tags": [ - "Group Admin" - ], - "summary": "Delete group", - "operationId": "deleteGroup", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "204": { - "description": "Group deleted" - }, - "404": { - "description": "Group not found" - } - } - } - }, - "/search/executions": { - "get": { - "tags": [ - "Search" - ], - "summary": "Search executions with basic filters", - "operationId": "searchGet", - "parameters": [ - { - "name": "status", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "timeFrom", - "in": "query", - "required": false, - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "timeTo", - "in": "query", - "required": false, - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "correlationId", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "text", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "routeId", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "agentId", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "processorType", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "group", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "offset", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - } - }, - { - "name": "limit", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 50 - } - }, - { - "name": "sortField", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "sortDir", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/SearchResultExecutionSummary" - } - } - } - } - } - }, - "post": { - "tags": [ - "Search" - ], - "summary": "Advanced search with all filters", - "operationId": "searchPost", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SearchRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/SearchResultExecutionSummary" - } - } - } - } - } - } - }, - "/data/metrics": { - "post": { - "tags": [ - "Ingestion" - ], - "summary": "Ingest agent metrics", - "description": "Accepts an array of MetricsSnapshot objects", - "operationId": "ingestMetrics", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - }, - "required": true - }, - "responses": { - "202": { - "description": "Data accepted for processing" - }, - "503": { - "description": "Buffer full, retry later" - } - } - } - }, - "/data/executions": { - "post": { - "tags": [ - "Ingestion" - ], - "summary": "Ingest route execution data", - "description": "Accepts a single RouteExecution or an array of RouteExecutions", - "operationId": "ingestExecutions", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - }, - "required": true - }, - "responses": { - "202": { - "description": "Data accepted for processing" - } - } - } - }, - "/data/diagrams": { - "post": { - "tags": [ - "Ingestion" - ], - "summary": "Ingest route diagram data", - "description": "Accepts a single RouteGraph or an array of RouteGraphs", - "operationId": "ingestDiagrams", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - }, - "required": true - }, - "responses": { - "202": { - "description": "Data accepted for processing" - } - } - } - }, - "/auth/refresh": { - "post": { - "tags": [ - "Authentication" - ], - "summary": "Refresh access token", - "operationId": "refresh", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RefreshRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Token refreshed", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/AuthTokenResponse" - } - } - } - }, - "401": { - "description": "Invalid refresh token", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/auth/oidc/callback": { - "post": { - "tags": [ - "Authentication" - ], - "summary": "Exchange OIDC authorization code for JWTs", - "operationId": "callback", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CallbackRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Authentication successful", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/AuthTokenResponse" - } - } - } - }, - "401": { - "description": "OIDC authentication failed", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "403": { - "description": "Account not provisioned", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "404": { - "description": "OIDC not configured or disabled", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/AuthTokenResponse" - } - } - } - } - } - } - }, - "/auth/login": { - "post": { - "tags": [ - "Authentication" - ], - "summary": "Login with local credentials", - "operationId": "login", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LoginRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Login successful", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/AuthTokenResponse" - } - } - } - }, - "401": { - "description": "Invalid credentials", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/agents/{id}/refresh": { - "post": { - "tags": [ - "Agent Management" - ], - "summary": "Refresh access token", - "description": "Issues a new access JWT from a valid refresh token", - "operationId": "refresh_1", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AgentRefreshRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "New access token issued", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/AgentRefreshResponse" - } - } - } - }, - "401": { - "description": "Invalid or expired refresh token", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/AgentRefreshResponse" - } - } - } - }, - "404": { - "description": "Agent not found", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/AgentRefreshResponse" - } - } - } - } - } - } - }, - "/agents/{id}/heartbeat": { - "post": { - "tags": [ - "Agent Management" - ], - "summary": "Agent heartbeat ping", - "description": "Updates the agent's last heartbeat timestamp", - "operationId": "heartbeat", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Heartbeat accepted" - }, - "404": { - "description": "Agent not registered" - } - } - } - }, - "/agents/{id}/commands": { - "post": { - "tags": [ - "Agent Commands" - ], - "summary": "Send command to a specific agent", - "description": "Sends a config-update, deep-trace, or replay command to the specified agent", - "operationId": "sendCommand", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CommandRequest" - } - } - }, - "required": true - }, - "responses": { - "202": { - "description": "Command accepted", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/CommandSingleResponse" - } - } - } - }, - "400": { - "description": "Invalid command payload", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/CommandSingleResponse" - } - } - } - }, - "404": { - "description": "Agent not registered", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/CommandSingleResponse" - } - } - } - } - } - } - }, - "/agents/{id}/commands/{commandId}/ack": { - "post": { - "tags": [ - "Agent Commands" - ], - "summary": "Acknowledge command receipt", - "description": "Agent acknowledges that it has received and processed a command", - "operationId": "acknowledgeCommand", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "commandId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Command acknowledged" - }, - "404": { - "description": "Command not found" - } - } - } - }, - "/agents/register": { - "post": { - "tags": [ - "Agent Management" - ], - "summary": "Register an agent", - "description": "Registers a new agent or re-registers an existing one. Requires bootstrap token in Authorization header.", - "operationId": "register", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AgentRegistrationRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Agent registered successfully", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/AgentRegistrationResponse" - } - } - } - }, - "400": { - "description": "Invalid registration payload", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - }, - "401": { - "description": "Missing or invalid bootstrap token", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/AgentRegistrationResponse" - } - } - } - } - } - } - }, - "/agents/groups/{group}/commands": { - "post": { - "tags": [ - "Agent Commands" - ], - "summary": "Send command to all agents in a group", - "description": "Sends a command to all LIVE agents in the specified group", - "operationId": "sendGroupCommand", - "parameters": [ - { - "name": "group", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CommandRequest" - } - } - }, - "required": true - }, - "responses": { - "202": { - "description": "Commands accepted", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/CommandBroadcastResponse" - } - } - } - }, - "400": { - "description": "Invalid command payload", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/CommandBroadcastResponse" - } - } - } - } - } - } - }, - "/agents/commands": { - "post": { - "tags": [ - "Agent Commands" - ], - "summary": "Broadcast command to all live agents", - "description": "Sends a command to all agents currently in LIVE state", - "operationId": "broadcastCommand", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CommandRequest" - } - } - }, - "required": true - }, - "responses": { - "202": { - "description": "Commands accepted", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/CommandBroadcastResponse" - } - } - } - }, - "400": { - "description": "Invalid command payload", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/CommandBroadcastResponse" - } - } - } - } - } - } - }, - "/admin/users": { - "get": { - "tags": [ - "User Admin" - ], - "summary": "List all users with RBAC detail", - "operationId": "listUsers", - "responses": { - "200": { - "description": "User list returned", - "content": { - "*/*": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UserDetail" - } - } - } - } - } - } - }, - "post": { - "tags": [ - "User Admin" - ], - "summary": "Create a local user", - "operationId": "createUser", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateUserRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "User created", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/UserDetail" - } - } - } - } - } - } - }, - "/admin/users/{userId}/roles/{roleId}": { - "post": { - "tags": [ - "User Admin" - ], - "summary": "Assign a role to a user", - "operationId": "assignRoleToUser", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "roleId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Role assigned" - }, - "404": { - "description": "User or role not found" - } - } - }, - "delete": { - "tags": [ - "User Admin" - ], - "summary": "Remove a role from a user", - "operationId": "removeRoleFromUser", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "roleId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "204": { - "description": "Role removed" - } - } - } - }, - "/admin/users/{userId}/groups/{groupId}": { - "post": { - "tags": [ - "User Admin" - ], - "summary": "Add a user to a group", - "operationId": "addUserToGroup", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "groupId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "User added to group" - } - } - }, - "delete": { - "tags": [ - "User Admin" - ], - "summary": "Remove a user from a group", - "operationId": "removeUserFromGroup", - "parameters": [ - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "groupId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "204": { - "description": "User removed from group" - } - } - } - }, - "/admin/roles": { - "get": { - "tags": [ - "Role Admin" - ], - "summary": "List all roles (system and custom)", - "operationId": "listRoles", - "responses": { - "200": { - "description": "Role list returned", - "content": { - "*/*": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RoleDetail" - } - } - } - } - } - } - }, - "post": { - "tags": [ - "Role Admin" - ], - "summary": "Create a custom role", - "operationId": "createRole", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateRoleRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Role created", - "content": { - "*/*": { - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "format": "uuid" - } - } - } - } - } - } - } - }, - "/admin/oidc/test": { - "post": { - "tags": [ - "OIDC Config Admin" - ], - "summary": "Test OIDC provider connectivity", - "operationId": "testConnection", - "responses": { - "200": { - "description": "Provider reachable", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/OidcTestResult" - } - } - } - }, - "400": { - "description": "Provider unreachable or misconfigured", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/admin/groups": { - "get": { - "tags": [ - "Group Admin" - ], - "summary": "List all groups with hierarchy and effective roles", - "operationId": "listGroups", - "responses": { - "200": { - "description": "Group list returned", - "content": { - "*/*": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GroupDetail" - } - } - } - } - } - } - }, - "post": { - "tags": [ - "Group Admin" - ], - "summary": "Create a new group", - "operationId": "createGroup", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateGroupRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "Group created", - "content": { - "*/*": { - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "format": "uuid" - } - } - } - } - } - } - } - }, - "/admin/groups/{id}/roles/{roleId}": { - "post": { - "tags": [ - "Group Admin" - ], - "summary": "Assign a role to a group", - "operationId": "assignRoleToGroup", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "roleId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "200": { - "description": "Role assigned to group" - }, - "404": { - "description": "Group not found" - } - } - }, - "delete": { - "tags": [ - "Group Admin" - ], - "summary": "Remove a role from a group", - "operationId": "removeRoleFromGroup", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - }, - { - "name": "roleId", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "uuid" - } - } - ], - "responses": { - "204": { - "description": "Role removed from group" - }, - "404": { - "description": "Group not found" - } - } - } - }, - "/admin/database/queries/{pid}/kill": { - "post": { - "tags": [ - "Database Admin" - ], - "summary": "Terminate a query by PID", - "operationId": "killQuery", - "parameters": [ - { - "name": "pid", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/search/stats": { - "get": { - "tags": [ - "Search" - ], - "summary": "Aggregate execution stats (P99 latency, active count)", - "operationId": "stats", - "parameters": [ - { - "name": "from", - "in": "query", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "to", - "in": "query", - "required": false, - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "routeId", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "group", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ExecutionStats" - } - } - } - } - } - } - }, - "/search/stats/timeseries": { - "get": { - "tags": [ - "Search" - ], - "summary": "Bucketed time-series stats over a time window", - "operationId": "timeseries", - "parameters": [ - { - "name": "from", - "in": "query", - "required": true, - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "to", - "in": "query", - "required": false, - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "name": "buckets", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 24 - } - }, - { - "name": "routeId", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "group", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/StatsTimeseries" - } - } - } - } - } - } - }, - "/routes/metrics": { - "get": { - "tags": [ - "Route Metrics" - ], - "summary": "Get route metrics", - "description": "Returns aggregated performance metrics per route for the given time window", - "operationId": "getMetrics", - "parameters": [ - { - "name": "from", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "to", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "appId", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Metrics returned", - "content": { - "*/*": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RouteMetrics" - } - } - } - } - } - } - } - }, - "/routes/catalog": { - "get": { - "tags": [ - "Route Catalog" - ], - "summary": "Get route catalog", - "description": "Returns all applications with their routes, agents, and health status", - "operationId": "getCatalog", - "responses": { - "200": { - "description": "Catalog returned", - "content": { - "*/*": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AppCatalogEntry" - } - } - } - } - } - } - } - }, - "/executions/{executionId}": { - "get": { - "tags": [ - "Detail" - ], - "summary": "Get execution detail with nested processor tree", - "operationId": "getDetail", - "parameters": [ - { - "name": "executionId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Execution detail found", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ExecutionDetail" - } - } - } - }, - "404": { - "description": "Execution not found", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ExecutionDetail" - } - } - } - } - } - } - }, - "/executions/{executionId}/processors/{index}/snapshot": { - "get": { - "tags": [ - "Detail" - ], - "summary": "Get exchange snapshot for a specific processor", - "operationId": "getProcessorSnapshot", - "parameters": [ - { - "name": "executionId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "index", - "in": "path", - "required": true, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "Snapshot data", - "content": { - "*/*": { - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - }, - "404": { - "description": "Snapshot not found", - "content": { - "*/*": { - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - } - } - }, - "/diagrams": { - "get": { - "tags": [ - "Diagrams" - ], - "summary": "Find diagram by application group and route ID", - "description": "Resolves group to agent IDs and finds the latest diagram for the route", - "operationId": "findByGroupAndRoute", - "parameters": [ - { - "name": "group", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "routeId", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Diagram layout returned", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/DiagramLayout" - } - } - } - }, - "404": { - "description": "No diagram found for the given group and route", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/DiagramLayout" - } - } - } - } - } - } - }, - "/diagrams/{contentHash}/render": { - "get": { - "tags": [ - "Diagrams" - ], - "summary": "Render a route diagram", - "description": "Returns SVG (default) or JSON layout based on Accept header", - "operationId": "renderDiagram", - "parameters": [ - { - "name": "contentHash", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Diagram rendered successfully", - "content": { - "image/svg+xml": { - "schema": { - "type": "string" - } - }, - "application/json": { - "schema": { - "$ref": "#/components/schemas/DiagramLayout" - } - } - } - }, - "404": { - "description": "Diagram not found", - "content": { - "*/*": { - "schema": { - "type": "object" - } - } - } - } - } - } - }, - "/auth/oidc/config": { - "get": { - "tags": [ - "Authentication" - ], - "summary": "Get OIDC config for SPA login flow", - "operationId": "getConfig_1", - "responses": { - "200": { - "description": "OIDC configuration", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/OidcPublicConfigResponse" - } - } - } - }, - "404": { - "description": "OIDC not configured or disabled", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/OidcPublicConfigResponse" - } - } - } - }, - "500": { - "description": "Failed to retrieve OIDC provider metadata", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/agents": { - "get": { - "tags": [ - "Agent Management" - ], - "summary": "List all agents", - "description": "Returns all registered agents with runtime metrics, optionally filtered by status and/or group", - "operationId": "listAgents", - "parameters": [ - { - "name": "status", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "group", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Agent list returned", - "content": { - "*/*": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AgentInstanceResponse" - } - } - } - } - }, - "400": { - "description": "Invalid status filter", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - } - } - } - } - } - } - }, - "/agents/{id}/events": { - "get": { - "tags": [ - "Agent SSE" - ], - "summary": "Open SSE event stream", - "description": "Opens a Server-Sent Events stream for the specified agent. Commands (config-update, deep-trace, replay) are pushed as events. Ping keepalive comments sent every 15 seconds.", - "operationId": "events", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "Last-Event-ID", - "in": "header", - "description": "Last received event ID (no replay, acknowledged only)", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "SSE stream opened", - "content": { - "text/event-stream": { - "schema": { - "$ref": "#/components/schemas/SseEmitter" - } - } - } - }, - "404": { - "description": "Agent not registered", - "content": { - "text/event-stream": { - "schema": { - "$ref": "#/components/schemas/SseEmitter" - } - } - } - } - } - } - }, - "/agents/events-log": { - "get": { - "tags": [ - "Agent Events" - ], - "summary": "Query agent events", - "description": "Returns agent lifecycle events, optionally filtered by app and/or agent ID", - "operationId": "getEvents", - "parameters": [ - { - "name": "appId", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "agentId", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "from", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "to", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "limit", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 50 - } - } - ], - "responses": { - "200": { - "description": "Events returned", - "content": { - "*/*": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AgentEventResponse" - } - } - } - } - } - } - } - }, - "/admin/rbac/stats": { - "get": { - "tags": [ - "RBAC Stats" - ], - "summary": "Get RBAC statistics for the dashboard", - "operationId": "getStats", - "responses": { - "200": { - "description": "RBAC stats returned", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/RbacStats" - } - } - } - } - } - } - }, - "/admin/opensearch/status": { - "get": { - "tags": [ - "OpenSearch Admin" - ], - "summary": "Get OpenSearch cluster status and version", - "operationId": "getStatus", - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/OpenSearchStatusResponse" - } - } - } - } - } - } - }, - "/admin/opensearch/pipeline": { - "get": { - "tags": [ - "OpenSearch Admin" - ], - "summary": "Get indexing pipeline statistics", - "operationId": "getPipeline", - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/PipelineStatsResponse" - } - } - } - } - } - } - }, - "/admin/opensearch/performance": { - "get": { - "tags": [ - "OpenSearch Admin" - ], - "summary": "Get OpenSearch performance metrics", - "operationId": "getPerformance", - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/PerformanceResponse" - } - } - } - } - } - } - }, - "/admin/opensearch/indices": { - "get": { - "tags": [ - "OpenSearch Admin" - ], - "summary": "Get OpenSearch indices with pagination", - "operationId": "getIndices", - "parameters": [ - { - "name": "page", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - } - }, - { - "name": "size", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 20 - } - }, - { - "name": "search", - "in": "query", - "required": false, - "schema": { - "type": "string", - "default": "" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/IndicesPageResponse" - } - } - } - } - } - } - }, - "/admin/database/tables": { - "get": { - "tags": [ - "Database Admin" - ], - "summary": "Get table sizes and row counts", - "operationId": "getTables", - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TableSizeResponse" - } - } - } - } - } - } - } - }, - "/admin/database/status": { - "get": { - "tags": [ - "Database Admin" - ], - "summary": "Get database connection status and version", - "operationId": "getStatus_1", - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/DatabaseStatusResponse" - } - } - } - } - } - } - }, - "/admin/database/queries": { - "get": { - "tags": [ - "Database Admin" - ], - "summary": "Get active queries", - "operationId": "getQueries", - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ActiveQueryResponse" - } - } - } - } - } - } - } - }, - "/admin/database/pool": { - "get": { - "tags": [ - "Database Admin" - ], - "summary": "Get HikariCP connection pool stats", - "operationId": "getPool", - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/ConnectionPoolResponse" - } - } - } - } - } - } - }, - "/admin/audit": { - "get": { - "tags": [ - "Audit Log" - ], - "summary": "Search audit log entries with pagination", - "operationId": "getAuditLog", - "parameters": [ - { - "name": "username", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "category", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "search", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "from", - "in": "query", - "required": false, - "schema": { - "type": "string", - "format": "date" - } - }, - { - "name": "to", - "in": "query", - "required": false, - "schema": { - "type": "string", - "format": "date" - } - }, - { - "name": "sort", - "in": "query", - "required": false, - "schema": { - "type": "string", - "default": "timestamp" - } - }, - { - "name": "order", - "in": "query", - "required": false, - "schema": { - "type": "string", - "default": "desc" - } - }, - { - "name": "page", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 0 - } - }, - { - "name": "size", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "format": "int32", - "default": 25 - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "*/*": { - "schema": { - "$ref": "#/components/schemas/AuditLogPageResponse" - } - } - } - } - } - } - }, - "/admin/opensearch/indices/{name}": { - "delete": { - "tags": [ - "OpenSearch Admin" - ], - "summary": "Delete an OpenSearch index", - "operationId": "deleteIndex", - "parameters": [ - { - "name": "name", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - } - }, - "components": { - "schemas": { - "UpdateUserRequest": { - "type": "object", - "properties": { - "displayName": { - "type": "string" - }, - "email": { - "type": "string" - } - } - }, - "DatabaseThresholdsRequest": { - "type": "object", - "description": "Database monitoring thresholds", - "properties": { - "connectionPoolWarning": { - "type": "integer", - "format": "int32", - "description": "Connection pool usage warning threshold (percentage)", - "maximum": 100, - "minimum": 0 - }, - "connectionPoolCritical": { - "type": "integer", - "format": "int32", - "description": "Connection pool usage critical threshold (percentage)", - "maximum": 100, - "minimum": 0 - }, - "queryDurationWarning": { - "type": "number", - "format": "double", - "description": "Query duration warning threshold (seconds)" - }, - "queryDurationCritical": { - "type": "number", - "format": "double", - "description": "Query duration critical threshold (seconds)" - } - } - }, - "OpenSearchThresholdsRequest": { - "type": "object", - "description": "OpenSearch monitoring thresholds", - "properties": { - "clusterHealthWarning": { - "type": "string", - "description": "Cluster health warning threshold (GREEN, YELLOW, RED)", - "minLength": 1 - }, - "clusterHealthCritical": { - "type": "string", - "description": "Cluster health critical threshold (GREEN, YELLOW, RED)", - "minLength": 1 - }, - "queueDepthWarning": { - "type": "integer", - "format": "int32", - "description": "Queue depth warning threshold", - "minimum": 0 - }, - "queueDepthCritical": { - "type": "integer", - "format": "int32", - "description": "Queue depth critical threshold", - "minimum": 0 - }, - "jvmHeapWarning": { - "type": "integer", - "format": "int32", - "description": "JVM heap usage warning threshold (percentage)", - "maximum": 100, - "minimum": 0 - }, - "jvmHeapCritical": { - "type": "integer", - "format": "int32", - "description": "JVM heap usage critical threshold (percentage)", - "maximum": 100, - "minimum": 0 - }, - "failedDocsWarning": { - "type": "integer", - "format": "int32", - "description": "Failed document count warning threshold", - "minimum": 0 - }, - "failedDocsCritical": { - "type": "integer", - "format": "int32", - "description": "Failed document count critical threshold", - "minimum": 0 - } - } - }, - "ThresholdConfigRequest": { - "type": "object", - "description": "Threshold configuration for admin monitoring", - "properties": { - "database": { - "$ref": "#/components/schemas/DatabaseThresholdsRequest" - }, - "opensearch": { - "$ref": "#/components/schemas/OpenSearchThresholdsRequest" - } - }, - "required": [ - "database", - "opensearch" - ] - }, - "DatabaseThresholds": { - "type": "object", - "properties": { - "connectionPoolWarning": { - "type": "integer", - "format": "int32" - }, - "connectionPoolCritical": { - "type": "integer", - "format": "int32" - }, - "queryDurationWarning": { - "type": "number", - "format": "double" - }, - "queryDurationCritical": { - "type": "number", - "format": "double" - } - } - }, - "OpenSearchThresholds": { - "type": "object", - "properties": { - "clusterHealthWarning": { - "type": "string" - }, - "clusterHealthCritical": { - "type": "string" - }, - "queueDepthWarning": { - "type": "integer", - "format": "int32" - }, - "queueDepthCritical": { - "type": "integer", - "format": "int32" - }, - "jvmHeapWarning": { - "type": "integer", - "format": "int32" - }, - "jvmHeapCritical": { - "type": "integer", - "format": "int32" - }, - "failedDocsWarning": { - "type": "integer", - "format": "int32" - }, - "failedDocsCritical": { - "type": "integer", - "format": "int32" - } - } - }, - "ThresholdConfig": { - "type": "object", - "properties": { - "database": { - "$ref": "#/components/schemas/DatabaseThresholds" - }, - "opensearch": { - "$ref": "#/components/schemas/OpenSearchThresholds" - } - } - }, - "UpdateRoleRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "scope": { - "type": "string" - } - } - }, - "OidcAdminConfigRequest": { - "type": "object", - "description": "OIDC configuration update request", - "properties": { - "enabled": { - "type": "boolean" - }, - "issuerUri": { - "type": "string" - }, - "clientId": { - "type": "string" - }, - "clientSecret": { - "type": "string" - }, - "rolesClaim": { - "type": "string" - }, - "defaultRoles": { - "type": "array", - "items": { - "type": "string" - } - }, - "autoSignup": { - "type": "boolean" - }, - "displayNameClaim": { - "type": "string" - } - } - }, - "ErrorResponse": { - "type": "object", - "description": "Error response", - "properties": { - "message": { - "type": "string" - } - }, - "required": [ - "message" - ] - }, - "OidcAdminConfigResponse": { - "type": "object", - "description": "OIDC configuration for admin management", - "properties": { - "configured": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - }, - "issuerUri": { - "type": "string" - }, - "clientId": { - "type": "string" - }, - "clientSecretSet": { - "type": "boolean" - }, - "rolesClaim": { - "type": "string" - }, - "defaultRoles": { - "type": "array", - "items": { - "type": "string" - } - }, - "autoSignup": { - "type": "boolean" - }, - "displayNameClaim": { - "type": "string" - } - } - }, - "UpdateGroupRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "parentGroupId": { - "type": "string", - "format": "uuid" - } - } - }, - "SearchRequest": { - "type": "object", - "properties": { - "status": { - "type": "string" - }, - "timeFrom": { - "type": "string", - "format": "date-time" - }, - "timeTo": { - "type": "string", - "format": "date-time" - }, - "durationMin": { - "type": "integer", - "format": "int64" - }, - "durationMax": { - "type": "integer", - "format": "int64" - }, - "correlationId": { - "type": "string" - }, - "text": { - "type": "string" - }, - "textInBody": { - "type": "string" - }, - "textInHeaders": { - "type": "string" - }, - "textInErrors": { - "type": "string" - }, - "routeId": { - "type": "string" - }, - "agentId": { - "type": "string" - }, - "processorType": { - "type": "string" - }, - "group": { - "type": "string" - }, - "agentIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "offset": { - "type": "integer", - "format": "int32" - }, - "limit": { - "type": "integer", - "format": "int32" - }, - "sortField": { - "type": "string" - }, - "sortDir": { - "type": "string" - } - } - }, - "ExecutionSummary": { - "type": "object", - "properties": { - "executionId": { - "type": "string" - }, - "routeId": { - "type": "string" - }, - "agentId": { - "type": "string" - }, - "status": { - "type": "string" - }, - "startTime": { - "type": "string", - "format": "date-time" - }, - "endTime": { - "type": "string", - "format": "date-time" - }, - "durationMs": { - "type": "integer", - "format": "int64" - }, - "correlationId": { - "type": "string" - }, - "errorMessage": { - "type": "string" - }, - "diagramContentHash": { - "type": "string" - } - }, - "required": [ - "agentId", - "correlationId", - "diagramContentHash", - "durationMs", - "endTime", - "errorMessage", - "executionId", - "routeId", - "startTime", - "status" - ] - }, - "SearchResultExecutionSummary": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ExecutionSummary" - } - }, - "total": { - "type": "integer", - "format": "int64" - }, - "offset": { - "type": "integer", - "format": "int32" - }, - "limit": { - "type": "integer", - "format": "int32" - } - }, - "required": [ - "data", - "limit", - "offset", - "total" - ] - }, - "RefreshRequest": { - "type": "object", - "properties": { - "refreshToken": { - "type": "string" - } - } - }, - "AuthTokenResponse": { - "type": "object", - "description": "JWT token pair", - "properties": { - "accessToken": { - "type": "string" - }, - "refreshToken": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "idToken": { - "type": "string", - "description": "OIDC id_token for end-session logout (only present after OIDC login)" - } - }, - "required": [ - "accessToken", - "displayName", - "refreshToken" - ] - }, - "CallbackRequest": { - "type": "object", - "properties": { - "code": { - "type": "string" - }, - "redirectUri": { - "type": "string" - } - } - }, - "LoginRequest": { - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "password": { - "type": "string" - } - } - }, - "AgentRefreshRequest": { - "type": "object", - "description": "Agent token refresh request", - "properties": { - "refreshToken": { - "type": "string" - } - }, - "required": [ - "refreshToken" - ] - }, - "AgentRefreshResponse": { - "type": "object", - "description": "Refreshed access and refresh tokens", - "properties": { - "accessToken": { - "type": "string" - }, - "refreshToken": { - "type": "string" - } - }, - "required": [ - "accessToken", - "refreshToken" - ] - }, - "CommandRequest": { - "type": "object", - "description": "Command to send to agent(s)", - "properties": { - "type": { - "type": "string", - "description": "Command type: config-update, deep-trace, or replay" - }, - "payload": { - "type": "object", - "description": "Command payload JSON" - } - }, - "required": [ - "type" - ] - }, - "CommandSingleResponse": { - "type": "object", - "description": "Result of sending a command to a single agent", - "properties": { - "commandId": { - "type": "string" - }, - "status": { - "type": "string" - } - }, - "required": [ - "commandId", - "status" - ] - }, - "AgentRegistrationRequest": { - "type": "object", - "description": "Agent registration payload", - "properties": { - "agentId": { - "type": "string" - }, - "name": { - "type": "string" - }, - "group": { - "type": "string", - "default": "default" - }, - "version": { - "type": "string" - }, - "routeIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "capabilities": { - "type": "object", - "additionalProperties": { - "type": "object" - } - } - }, - "required": [ - "agentId", - "name" - ] - }, - "AgentRegistrationResponse": { - "type": "object", - "description": "Agent registration result with JWT tokens and SSE endpoint", - "properties": { - "agentId": { - "type": "string" - }, - "sseEndpoint": { - "type": "string" - }, - "heartbeatIntervalMs": { - "type": "integer", - "format": "int64" - }, - "serverPublicKey": { - "type": "string" - }, - "accessToken": { - "type": "string" - }, - "refreshToken": { - "type": "string" - } - }, - "required": [ - "accessToken", - "agentId", - "refreshToken", - "serverPublicKey", - "sseEndpoint" - ] - }, - "CommandBroadcastResponse": { - "type": "object", - "description": "Result of broadcasting a command to multiple agents", - "properties": { - "commandIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "targetCount": { - "type": "integer", - "format": "int32" - } - }, - "required": [ - "commandIds" - ] - }, - "CreateUserRequest": { - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "email": { - "type": "string" - }, - "password": { - "type": "string" - } - } - }, - "GroupSummary": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - } - } - }, - "RoleSummary": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "system": { - "type": "boolean" - }, - "source": { - "type": "string" - } - } - }, - "UserDetail": { - "type": "object", - "properties": { - "userId": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "email": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "directRoles": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RoleSummary" - } - }, - "directGroups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GroupSummary" - } - }, - "effectiveRoles": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RoleSummary" - } - }, - "effectiveGroups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GroupSummary" - } - } - } - }, - "CreateRoleRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "scope": { - "type": "string" - } - } - }, - "OidcTestResult": { - "type": "object", - "description": "OIDC provider connectivity test result", - "properties": { - "status": { - "type": "string" - }, - "authorizationEndpoint": { - "type": "string" - } - }, - "required": [ - "authorizationEndpoint", - "status" - ] - }, - "CreateGroupRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "parentGroupId": { - "type": "string", - "format": "uuid" - } - } - }, - "ExecutionStats": { - "type": "object", - "properties": { - "totalCount": { - "type": "integer", - "format": "int64" - }, - "failedCount": { - "type": "integer", - "format": "int64" - }, - "avgDurationMs": { - "type": "integer", - "format": "int64" - }, - "p99LatencyMs": { - "type": "integer", - "format": "int64" - }, - "activeCount": { - "type": "integer", - "format": "int64" - }, - "totalToday": { - "type": "integer", - "format": "int64" - }, - "prevTotalCount": { - "type": "integer", - "format": "int64" - }, - "prevFailedCount": { - "type": "integer", - "format": "int64" - }, - "prevAvgDurationMs": { - "type": "integer", - "format": "int64" - }, - "prevP99LatencyMs": { - "type": "integer", - "format": "int64" - } - }, - "required": [ - "activeCount", - "avgDurationMs", - "failedCount", - "p99LatencyMs", - "prevAvgDurationMs", - "prevFailedCount", - "prevP99LatencyMs", - "prevTotalCount", - "totalCount", - "totalToday" - ] - }, - "StatsTimeseries": { - "type": "object", - "properties": { - "buckets": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TimeseriesBucket" - } - } - }, - "required": [ - "buckets" - ] - }, - "TimeseriesBucket": { - "type": "object", - "properties": { - "time": { - "type": "string", - "format": "date-time" - }, - "totalCount": { - "type": "integer", - "format": "int64" - }, - "failedCount": { - "type": "integer", - "format": "int64" - }, - "avgDurationMs": { - "type": "integer", - "format": "int64" - }, - "p99DurationMs": { - "type": "integer", - "format": "int64" - }, - "activeCount": { - "type": "integer", - "format": "int64" - } - }, - "required": [ - "activeCount", - "avgDurationMs", - "failedCount", - "p99DurationMs", - "time", - "totalCount" - ] - }, - "RouteMetrics": { - "type": "object", - "description": "Aggregated route performance metrics", - "properties": { - "routeId": { - "type": "string" - }, - "appId": { - "type": "string" - }, - "exchangeCount": { - "type": "integer", - "format": "int64" - }, - "successRate": { - "type": "number", - "format": "double" - }, - "avgDurationMs": { - "type": "number", - "format": "double" - }, - "p99DurationMs": { - "type": "number", - "format": "double" - }, - "errorRate": { - "type": "number", - "format": "double" - }, - "throughputPerSec": { - "type": "number", - "format": "double" - }, - "sparkline": { - "type": "array", - "items": { - "type": "number", - "format": "double" - } - } - }, - "required": [ - "appId", - "avgDurationMs", - "errorRate", - "exchangeCount", - "p99DurationMs", - "routeId", - "sparkline", - "successRate", - "throughputPerSec" - ] - }, - "AgentSummary": { - "type": "object", - "description": "Summary of an agent instance for sidebar display", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "status": { - "type": "string" - }, - "tps": { - "type": "number", - "format": "double" - } - }, - "required": [ - "id", - "name", - "status", - "tps" - ] - }, - "AppCatalogEntry": { - "type": "object", - "description": "Application catalog entry with routes and agents", - "properties": { - "appId": { - "type": "string" - }, - "routes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RouteSummary" - } - }, - "agents": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AgentSummary" - } - }, - "agentCount": { - "type": "integer", - "format": "int32" - }, - "health": { - "type": "string" - }, - "exchangeCount": { - "type": "integer", - "format": "int64" - } - }, - "required": [ - "agentCount", - "agents", - "appId", - "exchangeCount", - "health", - "routes" - ] - }, - "RouteSummary": { - "type": "object", - "description": "Summary of a route within an application", - "properties": { - "routeId": { - "type": "string" - }, - "exchangeCount": { - "type": "integer", - "format": "int64" - }, - "lastSeen": { - "type": "string", - "format": "date-time" - } - }, - "required": [ - "exchangeCount", - "lastSeen", - "routeId" - ] - }, - "ExecutionDetail": { - "type": "object", - "properties": { - "executionId": { - "type": "string" - }, - "routeId": { - "type": "string" - }, - "agentId": { - "type": "string" - }, - "status": { - "type": "string" - }, - "startTime": { - "type": "string", - "format": "date-time" - }, - "endTime": { - "type": "string", - "format": "date-time" - }, - "durationMs": { - "type": "integer", - "format": "int64" - }, - "correlationId": { - "type": "string" - }, - "exchangeId": { - "type": "string" - }, - "errorMessage": { - "type": "string" - }, - "errorStackTrace": { - "type": "string" - }, - "diagramContentHash": { - "type": "string" - }, - "processors": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProcessorNode" - } - }, - "groupName": { - "type": "string" - }, - "children": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProcessorNode" - } - } - }, - "required": [ - "agentId", - "correlationId", - "diagramContentHash", - "durationMs", - "endTime", - "errorMessage", - "errorStackTrace", - "exchangeId", - "executionId", - "processors", - "routeId", - "startTime", - "status" - ] - }, - "ProcessorNode": { - "type": "object", - "properties": { - "processorId": { - "type": "string" - }, - "processorType": { - "type": "string" - }, - "status": { - "type": "string" - }, - "startTime": { - "type": "string", - "format": "date-time" - }, - "endTime": { - "type": "string", - "format": "date-time" - }, - "durationMs": { - "type": "integer", - "format": "int64" - }, - "diagramNodeId": { - "type": "string" - }, - "errorMessage": { - "type": "string" - }, - "errorStackTrace": { - "type": "string" - }, - "children": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProcessorNode" - } - } - }, - "required": [ - "children", - "diagramNodeId", - "durationMs", - "endTime", - "errorMessage", - "errorStackTrace", - "processorId", - "processorType", - "startTime", - "status" - ] - }, - "DiagramLayout": { - "type": "object", - "properties": { - "width": { - "type": "number", - "format": "double" - }, - "height": { - "type": "number", - "format": "double" - }, - "nodes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PositionedNode" - } - }, - "edges": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PositionedEdge" - } - } - } - }, - "PositionedEdge": { - "type": "object", - "properties": { - "sourceId": { - "type": "string" - }, - "targetId": { - "type": "string" - }, - "label": { - "type": "string" - }, - "points": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number", - "format": "double" - } - } - } - } - }, - "PositionedNode": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "label": { - "type": "string" - }, - "type": { - "type": "string" - }, - "x": { - "type": "number", - "format": "double" - }, - "y": { - "type": "number", - "format": "double" - }, - "width": { - "type": "number", - "format": "double" - }, - "height": { - "type": "number", - "format": "double" - } - } - }, - "OidcPublicConfigResponse": { - "type": "object", - "description": "OIDC configuration for SPA login flow", - "properties": { - "issuer": { - "type": "string" - }, - "clientId": { - "type": "string" - }, - "authorizationEndpoint": { - "type": "string" - }, - "endSessionEndpoint": { - "type": "string", - "description": "Present if the provider supports RP-initiated logout" - } - }, - "required": [ - "authorizationEndpoint", - "clientId", - "issuer" - ] - }, - "AgentInstanceResponse": { - "type": "object", - "description": "Agent instance summary with runtime metrics", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "group": { - "type": "string" - }, - "status": { - "type": "string" - }, - "routeIds": { - "type": "array", - "items": { - "type": "string" - } - }, - "registeredAt": { - "type": "string", - "format": "date-time" - }, - "lastHeartbeat": { - "type": "string", - "format": "date-time" - }, - "tps": { - "type": "number", - "format": "double" - }, - "errorRate": { - "type": "number", - "format": "double" - }, - "activeRoutes": { - "type": "integer", - "format": "int32" - }, - "totalRoutes": { - "type": "integer", - "format": "int32" - }, - "uptimeSeconds": { - "type": "integer", - "format": "int64" - } - }, - "required": [ - "activeRoutes", - "errorRate", - "group", - "id", - "lastHeartbeat", - "name", - "registeredAt", - "routeIds", - "status", - "totalRoutes", - "tps", - "uptimeSeconds" - ] - }, - "SseEmitter": { - "type": "object", - "properties": { - "timeout": { - "type": "integer", - "format": "int64" - } - } - }, - "AgentEventResponse": { - "type": "object", - "description": "Agent lifecycle event", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "agentId": { - "type": "string" - }, - "appId": { - "type": "string" - }, - "eventType": { - "type": "string" - }, - "detail": { - "type": "string" - }, - "timestamp": { - "type": "string", - "format": "date-time" - } - }, - "required": [ - "agentId", - "appId", - "detail", - "eventType", - "id", - "timestamp" - ] - }, - "RoleDetail": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "scope": { - "type": "string" - }, - "system": { - "type": "boolean" - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "assignedGroups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GroupSummary" - } - }, - "directUsers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UserSummary" - } - }, - "effectivePrincipals": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UserSummary" - } - } - } - }, - "UserSummary": { - "type": "object", - "properties": { - "userId": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "provider": { - "type": "string" - } - } - }, - "RbacStats": { - "type": "object", - "properties": { - "userCount": { - "type": "integer", - "format": "int32" - }, - "activeUserCount": { - "type": "integer", - "format": "int32" - }, - "groupCount": { - "type": "integer", - "format": "int32" - }, - "maxGroupDepth": { - "type": "integer", - "format": "int32" - }, - "roleCount": { - "type": "integer", - "format": "int32" - } - } - }, - "OpenSearchStatusResponse": { - "type": "object", - "description": "OpenSearch cluster status", - "properties": { - "reachable": { - "type": "boolean", - "description": "Whether the cluster is reachable" - }, - "clusterHealth": { - "type": "string", - "description": "Cluster health status (GREEN, YELLOW, RED)" - }, - "version": { - "type": "string", - "description": "OpenSearch version" - }, - "nodeCount": { - "type": "integer", - "format": "int32", - "description": "Number of nodes in the cluster" - }, - "host": { - "type": "string", - "description": "OpenSearch host" - } - } - }, - "PipelineStatsResponse": { - "type": "object", - "description": "Search indexing pipeline statistics", - "properties": { - "queueDepth": { - "type": "integer", - "format": "int32", - "description": "Current queue depth" - }, - "maxQueueSize": { - "type": "integer", - "format": "int32", - "description": "Maximum queue size" - }, - "failedCount": { - "type": "integer", - "format": "int64", - "description": "Number of failed indexing operations" - }, - "indexedCount": { - "type": "integer", - "format": "int64", - "description": "Number of successfully indexed documents" - }, - "debounceMs": { - "type": "integer", - "format": "int64", - "description": "Debounce interval in milliseconds" - }, - "indexingRate": { - "type": "number", - "format": "double", - "description": "Current indexing rate (docs/sec)" - }, - "lastIndexedAt": { - "type": "string", - "format": "date-time", - "description": "Timestamp of last indexed document" - } - } - }, - "PerformanceResponse": { - "type": "object", - "description": "OpenSearch performance metrics", - "properties": { - "queryCacheHitRate": { - "type": "number", - "format": "double", - "description": "Query cache hit rate (0.0-1.0)" - }, - "requestCacheHitRate": { - "type": "number", - "format": "double", - "description": "Request cache hit rate (0.0-1.0)" - }, - "searchLatencyMs": { - "type": "number", - "format": "double", - "description": "Average search latency in milliseconds" - }, - "indexingLatencyMs": { - "type": "number", - "format": "double", - "description": "Average indexing latency in milliseconds" - }, - "jvmHeapUsedBytes": { - "type": "integer", - "format": "int64", - "description": "JVM heap used in bytes" - }, - "jvmHeapMaxBytes": { - "type": "integer", - "format": "int64", - "description": "JVM heap max in bytes" - } - } - }, - "IndexInfoResponse": { - "type": "object", - "description": "OpenSearch index information", - "properties": { - "name": { - "type": "string", - "description": "Index name" - }, - "docCount": { - "type": "integer", - "format": "int64", - "description": "Document count" - }, - "size": { - "type": "string", - "description": "Human-readable index size" - }, - "sizeBytes": { - "type": "integer", - "format": "int64", - "description": "Index size in bytes" - }, - "health": { - "type": "string", - "description": "Index health status" - }, - "primaryShards": { - "type": "integer", - "format": "int32", - "description": "Number of primary shards" - }, - "replicaShards": { - "type": "integer", - "format": "int32", - "description": "Number of replica shards" - } - } - }, - "IndicesPageResponse": { - "type": "object", - "description": "Paginated list of OpenSearch indices", - "properties": { - "indices": { - "type": "array", - "description": "Index list for current page", - "items": { - "$ref": "#/components/schemas/IndexInfoResponse" - } - }, - "totalIndices": { - "type": "integer", - "format": "int64", - "description": "Total number of indices" - }, - "totalDocs": { - "type": "integer", - "format": "int64", - "description": "Total document count across all indices" - }, - "totalSize": { - "type": "string", - "description": "Human-readable total size" - }, - "page": { - "type": "integer", - "format": "int32", - "description": "Current page number (0-based)" - }, - "pageSize": { - "type": "integer", - "format": "int32", - "description": "Page size" - }, - "totalPages": { - "type": "integer", - "format": "int32", - "description": "Total number of pages" - } - } - }, - "GroupDetail": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "name": { - "type": "string" - }, - "parentGroupId": { - "type": "string", - "format": "uuid" - }, - "createdAt": { - "type": "string", - "format": "date-time" - }, - "directRoles": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RoleSummary" - } - }, - "effectiveRoles": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RoleSummary" - } - }, - "members": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UserSummary" - } - }, - "childGroups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GroupSummary" - } - } - } - }, - "TableSizeResponse": { - "type": "object", - "description": "Table size and row count information", - "properties": { - "tableName": { - "type": "string", - "description": "Table name" - }, - "rowCount": { - "type": "integer", - "format": "int64", - "description": "Approximate row count" - }, - "dataSize": { - "type": "string", - "description": "Human-readable data size" - }, - "indexSize": { - "type": "string", - "description": "Human-readable index size" - }, - "dataSizeBytes": { - "type": "integer", - "format": "int64", - "description": "Data size in bytes" - }, - "indexSizeBytes": { - "type": "integer", - "format": "int64", - "description": "Index size in bytes" - } - } - }, - "DatabaseStatusResponse": { - "type": "object", - "description": "Database connection and version status", - "properties": { - "connected": { - "type": "boolean", - "description": "Whether the database is reachable" - }, - "version": { - "type": "string", - "description": "PostgreSQL version string" - }, - "host": { - "type": "string", - "description": "Database host" - }, - "schema": { - "type": "string", - "description": "Current schema search path" - }, - "timescaleDb": { - "type": "boolean", - "description": "Whether TimescaleDB extension is available" - } - } - }, - "ActiveQueryResponse": { - "type": "object", - "description": "Currently running database query", - "properties": { - "pid": { - "type": "integer", - "format": "int32", - "description": "Backend process ID" - }, - "durationSeconds": { - "type": "number", - "format": "double", - "description": "Query duration in seconds" - }, - "state": { - "type": "string", - "description": "Backend state (active, idle, etc.)" - }, - "query": { - "type": "string", - "description": "SQL query text" - } - } - }, - "ConnectionPoolResponse": { - "type": "object", - "description": "HikariCP connection pool statistics", - "properties": { - "activeConnections": { - "type": "integer", - "format": "int32", - "description": "Number of currently active connections" - }, - "idleConnections": { - "type": "integer", - "format": "int32", - "description": "Number of idle connections" - }, - "pendingThreads": { - "type": "integer", - "format": "int32", - "description": "Number of threads waiting for a connection" - }, - "maxWaitMs": { - "type": "integer", - "format": "int64", - "description": "Maximum wait time in milliseconds" - }, - "maxPoolSize": { - "type": "integer", - "format": "int32", - "description": "Maximum pool size" - } - } - }, - "AuditLogPageResponse": { - "type": "object", - "description": "Paginated audit log entries", - "properties": { - "items": { - "type": "array", - "description": "Audit log entries", - "items": { - "$ref": "#/components/schemas/AuditRecord" - } - }, - "totalCount": { - "type": "integer", - "format": "int64", - "description": "Total number of matching entries" - }, - "page": { - "type": "integer", - "format": "int32", - "description": "Current page number (0-based)" - }, - "pageSize": { - "type": "integer", - "format": "int32", - "description": "Page size" - }, - "totalPages": { - "type": "integer", - "format": "int32", - "description": "Total number of pages" - } - } - }, - "AuditRecord": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "username": { - "type": "string" - }, - "action": { - "type": "string" - }, - "category": { - "type": "string", - "enum": [ - "INFRA", - "AUTH", - "USER_MGMT", - "CONFIG", - "RBAC" - ] - }, - "target": { - "type": "string" - }, - "detail": { - "type": "object", - "additionalProperties": { - "type": "object" - } - }, - "result": { - "type": "string", - "enum": [ - "SUCCESS", - "FAILURE" - ] - }, - "ipAddress": { - "type": "string" - }, - "userAgent": { - "type": "string" - } - } - } - }, - "securitySchemes": { - "bearer": { - "type": "http", - "scheme": "bearer", - "bearerFormat": "JWT" - } - } - } -} +{"openapi":"3.1.0","info":{"title":"Cameleer3 Server API","version":"1.0"},"servers":[{"url":"/api/v1","description":"Relative"}],"security":[{"bearer":[]}],"tags":[{"name":"Agent Events","description":"Agent lifecycle event log"},{"name":"Database Admin","description":"Database monitoring and management (ADMIN only)"},{"name":"Threshold Admin","description":"Monitoring threshold configuration (ADMIN only)"},{"name":"Agent Commands","description":"Command push endpoints for agent communication"},{"name":"User Admin","description":"User management (ADMIN only)"},{"name":"Agent Management","description":"Agent registration and lifecycle endpoints"},{"name":"Authentication","description":"Login and token refresh endpoints"},{"name":"Role Admin","description":"Role management (ADMIN only)"},{"name":"RBAC Stats","description":"RBAC statistics (ADMIN only)"},{"name":"OIDC Config Admin","description":"OIDC provider configuration (ADMIN only)"},{"name":"Route Metrics","description":"Route performance metrics"},{"name":"Search","description":"Transaction search endpoints"},{"name":"Agent SSE","description":"Server-Sent Events endpoint for agent communication"},{"name":"Ingestion","description":"Data ingestion endpoints"},{"name":"Audit Log","description":"Audit log viewer (ADMIN only)"},{"name":"Group Admin","description":"Group management (ADMIN only)"},{"name":"Diagrams","description":"Diagram rendering endpoints"},{"name":"OpenSearch Admin","description":"OpenSearch monitoring and management (ADMIN only)"},{"name":"Detail","description":"Execution detail and processor snapshot endpoints"},{"name":"Route Catalog","description":"Route catalog and discovery"}],"paths":{"/admin/users/{userId}":{"get":{"tags":["User Admin"],"summary":"Get user by ID with RBAC detail","operationId":"getUser","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"User found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDetail"}}}},"404":{"description":"User not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDetail"}}}}}},"put":{"tags":["User Admin"],"summary":"Update user display name or email","operationId":"updateUser","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserRequest"}}},"required":true},"responses":{"200":{"description":"User updated"},"404":{"description":"User not found"}}},"delete":{"tags":["User Admin"],"summary":"Delete user","operationId":"deleteUser","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"User deleted"}}}},"/admin/thresholds":{"get":{"tags":["Threshold Admin"],"summary":"Get current threshold configuration","operationId":"getThresholds","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ThresholdConfig"}}}}}},"put":{"tags":["Threshold Admin"],"summary":"Update threshold configuration","operationId":"updateThresholds","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThresholdConfigRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ThresholdConfig"}}}}}}},"/admin/roles/{id}":{"get":{"tags":["Role Admin"],"summary":"Get role by ID with effective principals","operationId":"getRole","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Role found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RoleDetail"}}}},"404":{"description":"Role not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RoleDetail"}}}}}},"put":{"tags":["Role Admin"],"summary":"Update a custom role","operationId":"updateRole","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRoleRequest"}}},"required":true},"responses":{"200":{"description":"Role updated"},"403":{"description":"Cannot modify system role"},"404":{"description":"Role not found"}}},"delete":{"tags":["Role Admin"],"summary":"Delete a custom role","operationId":"deleteRole","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Role deleted"},"403":{"description":"Cannot delete system role"},"404":{"description":"Role not found"}}}},"/admin/oidc":{"get":{"tags":["OIDC Config Admin"],"summary":"Get OIDC configuration","operationId":"getConfig","responses":{"200":{"description":"Current OIDC configuration (client_secret masked)","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OidcAdminConfigResponse"}}}}}},"put":{"tags":["OIDC Config Admin"],"summary":"Save OIDC configuration","operationId":"saveConfig","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OidcAdminConfigRequest"}}},"required":true},"responses":{"200":{"description":"Configuration saved","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OidcAdminConfigResponse"}}}},"400":{"description":"Invalid configuration","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["OIDC Config Admin"],"summary":"Delete OIDC configuration","operationId":"deleteConfig","responses":{"204":{"description":"Configuration deleted"}}}},"/admin/groups/{id}":{"get":{"tags":["Group Admin"],"summary":"Get group by ID with effective roles","operationId":"getGroup","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Group found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GroupDetail"}}}},"404":{"description":"Group not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/GroupDetail"}}}}}},"put":{"tags":["Group Admin"],"summary":"Update group name or parent","operationId":"updateGroup","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGroupRequest"}}},"required":true},"responses":{"200":{"description":"Group updated"},"404":{"description":"Group not found"},"409":{"description":"Cycle detected in group hierarchy"}}},"delete":{"tags":["Group Admin"],"summary":"Delete group","operationId":"deleteGroup","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Group deleted"},"404":{"description":"Group not found"}}}},"/search/executions":{"get":{"tags":["Search"],"summary":"Search executions with basic filters","operationId":"searchGet","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string"}},{"name":"timeFrom","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"timeTo","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"correlationId","in":"query","required":false,"schema":{"type":"string"}},{"name":"text","in":"query","required":false,"schema":{"type":"string"}},{"name":"routeId","in":"query","required":false,"schema":{"type":"string"}},{"name":"agentId","in":"query","required":false,"schema":{"type":"string"}},{"name":"processorType","in":"query","required":false,"schema":{"type":"string"}},{"name":"application","in":"query","required":false,"schema":{"type":"string"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":0}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":50}},{"name":"sortField","in":"query","required":false,"schema":{"type":"string"}},{"name":"sortDir","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SearchResultExecutionSummary"}}}}}},"post":{"tags":["Search"],"summary":"Advanced search with all filters","operationId":"searchPost","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/SearchResultExecutionSummary"}}}}}}},"/data/metrics":{"post":{"tags":["Ingestion"],"summary":"Ingest agent metrics","description":"Accepts an array of MetricsSnapshot objects","operationId":"ingestMetrics","requestBody":{"content":{"application/json":{"schema":{"type":"string"}}},"required":true},"responses":{"202":{"description":"Data accepted for processing"},"503":{"description":"Buffer full, retry later"}}}},"/data/executions":{"post":{"tags":["Ingestion"],"summary":"Ingest route execution data","description":"Accepts a single RouteExecution or an array of RouteExecutions","operationId":"ingestExecutions","requestBody":{"content":{"application/json":{"schema":{"type":"string"}}},"required":true},"responses":{"202":{"description":"Data accepted for processing"}}}},"/data/diagrams":{"post":{"tags":["Ingestion"],"summary":"Ingest route diagram data","description":"Accepts a single RouteGraph or an array of RouteGraphs","operationId":"ingestDiagrams","requestBody":{"content":{"application/json":{"schema":{"type":"string"}}},"required":true},"responses":{"202":{"description":"Data accepted for processing"}}}},"/auth/refresh":{"post":{"tags":["Authentication"],"summary":"Refresh access token","operationId":"refresh","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}},"required":true},"responses":{"200":{"description":"Token refreshed","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AuthTokenResponse"}}}},"401":{"description":"Invalid refresh token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/auth/oidc/callback":{"post":{"tags":["Authentication"],"summary":"Exchange OIDC authorization code for JWTs","operationId":"callback","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallbackRequest"}}},"required":true},"responses":{"200":{"description":"Authentication successful","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AuthTokenResponse"}}}},"401":{"description":"OIDC authentication failed","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Account not provisioned","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"OIDC not configured or disabled","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AuthTokenResponse"}}}}}}},"/auth/login":{"post":{"tags":["Authentication"],"summary":"Login with local credentials","operationId":"login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Login successful","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AuthTokenResponse"}}}},"401":{"description":"Invalid credentials","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/agents/{id}/refresh":{"post":{"tags":["Agent Management"],"summary":"Refresh access token","description":"Issues a new access JWT from a valid refresh token","operationId":"refresh_1","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRefreshRequest"}}},"required":true},"responses":{"200":{"description":"New access token issued","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AgentRefreshResponse"}}}},"401":{"description":"Invalid or expired refresh token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AgentRefreshResponse"}}}},"404":{"description":"Agent not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AgentRefreshResponse"}}}}}}},"/agents/{id}/heartbeat":{"post":{"tags":["Agent Management"],"summary":"Agent heartbeat ping","description":"Updates the agent's last heartbeat timestamp","operationId":"heartbeat","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Heartbeat accepted"},"404":{"description":"Agent not registered"}}}},"/agents/{id}/commands":{"post":{"tags":["Agent Commands"],"summary":"Send command to a specific agent","description":"Sends a config-update, deep-trace, or replay command to the specified agent","operationId":"sendCommand","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandRequest"}}},"required":true},"responses":{"202":{"description":"Command accepted","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CommandSingleResponse"}}}},"400":{"description":"Invalid command payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CommandSingleResponse"}}}},"404":{"description":"Agent not registered","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CommandSingleResponse"}}}}}}},"/agents/{id}/commands/{commandId}/ack":{"post":{"tags":["Agent Commands"],"summary":"Acknowledge command receipt","description":"Agent acknowledges that it has received and processed a command","operationId":"acknowledgeCommand","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"commandId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Command acknowledged"},"404":{"description":"Command not found"}}}},"/agents/register":{"post":{"tags":["Agent Management"],"summary":"Register an agent","description":"Registers a new agent or re-registers an existing one. Requires bootstrap token in Authorization header.","operationId":"register","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentRegistrationRequest"}}},"required":true},"responses":{"200":{"description":"Agent registered successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AgentRegistrationResponse"}}}},"400":{"description":"Invalid registration payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bootstrap token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AgentRegistrationResponse"}}}}}}},"/agents/groups/{group}/commands":{"post":{"tags":["Agent Commands"],"summary":"Send command to all agents in a group","description":"Sends a command to all LIVE agents in the specified group","operationId":"sendGroupCommand","parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandRequest"}}},"required":true},"responses":{"202":{"description":"Commands accepted","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CommandBroadcastResponse"}}}},"400":{"description":"Invalid command payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CommandBroadcastResponse"}}}}}}},"/agents/commands":{"post":{"tags":["Agent Commands"],"summary":"Broadcast command to all live agents","description":"Sends a command to all agents currently in LIVE state","operationId":"broadcastCommand","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandRequest"}}},"required":true},"responses":{"202":{"description":"Commands accepted","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CommandBroadcastResponse"}}}},"400":{"description":"Invalid command payload","content":{"*/*":{"schema":{"$ref":"#/components/schemas/CommandBroadcastResponse"}}}}}}},"/admin/users":{"get":{"tags":["User Admin"],"summary":"List all users with RBAC detail","operationId":"listUsers","responses":{"200":{"description":"User list returned","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDetail"}}}}}}},"post":{"tags":["User Admin"],"summary":"Create a local user","operationId":"createUser","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserRequest"}}},"required":true},"responses":{"200":{"description":"User created","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserDetail"}}}}}}},"/admin/users/{userId}/roles/{roleId}":{"post":{"tags":["User Admin"],"summary":"Assign a role to a user","operationId":"assignRoleToUser","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string"}},{"name":"roleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Role assigned"},"404":{"description":"User or role not found"}}},"delete":{"tags":["User Admin"],"summary":"Remove a role from a user","operationId":"removeRoleFromUser","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string"}},{"name":"roleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Role removed"}}}},"/admin/users/{userId}/password":{"post":{"tags":["User Admin"],"summary":"Reset user password","operationId":"resetPassword","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPasswordRequest"}}},"required":true},"responses":{"204":{"description":"Password reset"}}}},"/admin/users/{userId}/groups/{groupId}":{"post":{"tags":["User Admin"],"summary":"Add a user to a group","operationId":"addUserToGroup","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string"}},{"name":"groupId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"User added to group"}}},"delete":{"tags":["User Admin"],"summary":"Remove a user from a group","operationId":"removeUserFromGroup","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string"}},{"name":"groupId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"User removed from group"}}}},"/admin/roles":{"get":{"tags":["Role Admin"],"summary":"List all roles (system and custom)","operationId":"listRoles","responses":{"200":{"description":"Role list returned","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RoleDetail"}}}}}}},"post":{"tags":["Role Admin"],"summary":"Create a custom role","operationId":"createRole","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRoleRequest"}}},"required":true},"responses":{"200":{"description":"Role created","content":{"*/*":{"schema":{"type":"object","additionalProperties":{"type":"string","format":"uuid"}}}}}}}},"/admin/oidc/test":{"post":{"tags":["OIDC Config Admin"],"summary":"Test OIDC provider connectivity","operationId":"testConnection","responses":{"200":{"description":"Provider reachable","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OidcTestResult"}}}},"400":{"description":"Provider unreachable or misconfigured","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/admin/groups":{"get":{"tags":["Group Admin"],"summary":"List all groups with hierarchy and effective roles","operationId":"listGroups","responses":{"200":{"description":"Group list returned","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GroupDetail"}}}}}}},"post":{"tags":["Group Admin"],"summary":"Create a new group","operationId":"createGroup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGroupRequest"}}},"required":true},"responses":{"200":{"description":"Group created","content":{"*/*":{"schema":{"type":"object","additionalProperties":{"type":"string","format":"uuid"}}}}}}}},"/admin/groups/{id}/roles/{roleId}":{"post":{"tags":["Group Admin"],"summary":"Assign a role to a group","operationId":"assignRoleToGroup","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"roleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Role assigned to group"},"404":{"description":"Group not found"}}},"delete":{"tags":["Group Admin"],"summary":"Remove a role from a group","operationId":"removeRoleFromGroup","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"roleId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Role removed from group"},"404":{"description":"Group not found"}}}},"/admin/database/queries/{pid}/kill":{"post":{"tags":["Database Admin"],"summary":"Terminate a query by PID","operationId":"killQuery","parameters":[{"name":"pid","in":"path","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"OK"}}}},"/search/stats":{"get":{"tags":["Search"],"summary":"Aggregate execution stats (P99 latency, active count)","operationId":"stats","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"routeId","in":"query","required":false,"schema":{"type":"string"}},{"name":"application","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ExecutionStats"}}}}}}},"/search/stats/timeseries":{"get":{"tags":["Search"],"summary":"Bucketed time-series stats over a time window","operationId":"timeseries","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"buckets","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":24}},{"name":"routeId","in":"query","required":false,"schema":{"type":"string"}},{"name":"application","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/StatsTimeseries"}}}}}}},"/routes/metrics":{"get":{"tags":["Route Metrics"],"summary":"Get route metrics","description":"Returns aggregated performance metrics per route for the given time window","operationId":"getMetrics","parameters":[{"name":"from","in":"query","required":false,"schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}},{"name":"appId","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Metrics returned","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RouteMetrics"}}}}}}}},"/routes/metrics/processors":{"get":{"tags":["Route Metrics"],"summary":"Get processor metrics","description":"Returns aggregated performance metrics per processor for the given route and time window","operationId":"getProcessorMetrics","parameters":[{"name":"routeId","in":"query","required":true,"schema":{"type":"string"}},{"name":"appId","in":"query","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Metrics returned","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProcessorMetrics"}}}}}}}},"/routes/catalog":{"get":{"tags":["Route Catalog"],"summary":"Get route catalog","description":"Returns all applications with their routes, agents, and health status","operationId":"getCatalog","responses":{"200":{"description":"Catalog returned","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AppCatalogEntry"}}}}}}}},"/executions/{executionId}":{"get":{"tags":["Detail"],"summary":"Get execution detail with nested processor tree","operationId":"getDetail","parameters":[{"name":"executionId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Execution detail found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ExecutionDetail"}}}},"404":{"description":"Execution not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ExecutionDetail"}}}}}}},"/executions/{executionId}/processors/{index}/snapshot":{"get":{"tags":["Detail"],"summary":"Get exchange snapshot for a specific processor","operationId":"getProcessorSnapshot","parameters":[{"name":"executionId","in":"path","required":true,"schema":{"type":"string"}},{"name":"index","in":"path","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"Snapshot data","content":{"*/*":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}},"404":{"description":"Snapshot not found","content":{"*/*":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/diagrams":{"get":{"tags":["Diagrams"],"summary":"Find diagram by application and route ID","description":"Resolves application to agent IDs and finds the latest diagram for the route","operationId":"findByApplicationAndRoute","parameters":[{"name":"application","in":"query","required":true,"schema":{"type":"string"}},{"name":"routeId","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Diagram layout returned","content":{"*/*":{"schema":{"$ref":"#/components/schemas/DiagramLayout"}}}},"404":{"description":"No diagram found for the given application and route","content":{"*/*":{"schema":{"$ref":"#/components/schemas/DiagramLayout"}}}}}}},"/diagrams/{contentHash}/render":{"get":{"tags":["Diagrams"],"summary":"Render a route diagram","description":"Returns SVG (default) or JSON layout based on Accept header","operationId":"renderDiagram","parameters":[{"name":"contentHash","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Diagram rendered successfully","content":{"image/svg+xml":{"schema":{"type":"string"}},"application/json":{"schema":{"$ref":"#/components/schemas/DiagramLayout"}}}},"404":{"description":"Diagram not found","content":{"*/*":{"schema":{"type":"object"}}}}}}},"/auth/oidc/config":{"get":{"tags":["Authentication"],"summary":"Get OIDC config for SPA login flow","operationId":"getConfig_1","responses":{"200":{"description":"OIDC configuration","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OidcPublicConfigResponse"}}}},"404":{"description":"OIDC not configured or disabled","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OidcPublicConfigResponse"}}}},"500":{"description":"Failed to retrieve OIDC provider metadata","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/agents":{"get":{"tags":["Agent Management"],"summary":"List all agents","description":"Returns all registered agents with runtime metrics, optionally filtered by status and/or application","operationId":"listAgents","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string"}},{"name":"application","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Agent list returned","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AgentInstanceResponse"}}}}},"400":{"description":"Invalid status filter","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/agents/{id}/events":{"get":{"tags":["Agent SSE"],"summary":"Open SSE event stream","description":"Opens a Server-Sent Events stream for the specified agent. Commands (config-update, deep-trace, replay) are pushed as events. Ping keepalive comments sent every 15 seconds.","operationId":"events","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Last-Event-ID","in":"header","description":"Last received event ID (no replay, acknowledged only)","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"SSE stream opened","content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/SseEmitter"}}}},"404":{"description":"Agent not registered","content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/SseEmitter"}}}}}}},"/agents/{agentId}/metrics":{"get":{"tags":["agent-metrics-controller"],"operationId":"getMetrics_1","parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}},{"name":"names","in":"query","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"buckets","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":60}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AgentMetricsResponse"}}}}}}},"/agents/events-log":{"get":{"tags":["Agent Events"],"summary":"Query agent events","description":"Returns agent lifecycle events, optionally filtered by app and/or agent ID","operationId":"getEvents","parameters":[{"name":"appId","in":"query","required":false,"schema":{"type":"string"}},{"name":"agentId","in":"query","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string"}},{"name":"to","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":50}}],"responses":{"200":{"description":"Events returned","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AgentEventResponse"}}}}}}}},"/admin/rbac/stats":{"get":{"tags":["RBAC Stats"],"summary":"Get RBAC statistics for the dashboard","operationId":"getStats","responses":{"200":{"description":"RBAC stats returned","content":{"*/*":{"schema":{"$ref":"#/components/schemas/RbacStats"}}}}}}},"/admin/opensearch/status":{"get":{"tags":["OpenSearch Admin"],"summary":"Get OpenSearch cluster status and version","operationId":"getStatus","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/OpenSearchStatusResponse"}}}}}}},"/admin/opensearch/pipeline":{"get":{"tags":["OpenSearch Admin"],"summary":"Get indexing pipeline statistics","operationId":"getPipeline","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PipelineStatsResponse"}}}}}}},"/admin/opensearch/performance":{"get":{"tags":["OpenSearch Admin"],"summary":"Get OpenSearch performance metrics","operationId":"getPerformance","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/PerformanceResponse"}}}}}}},"/admin/opensearch/indices":{"get":{"tags":["OpenSearch Admin"],"summary":"Get OpenSearch indices with pagination","operationId":"getIndices","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":0}},{"name":"size","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":20}},{"name":"search","in":"query","required":false,"schema":{"type":"string","default":""}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/IndicesPageResponse"}}}}}}},"/admin/database/tables":{"get":{"tags":["Database Admin"],"summary":"Get table sizes and row counts","operationId":"getTables","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TableSizeResponse"}}}}}}}},"/admin/database/status":{"get":{"tags":["Database Admin"],"summary":"Get database connection status and version","operationId":"getStatus_1","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/DatabaseStatusResponse"}}}}}}},"/admin/database/queries":{"get":{"tags":["Database Admin"],"summary":"Get active queries","operationId":"getQueries","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ActiveQueryResponse"}}}}}}}},"/admin/database/pool":{"get":{"tags":["Database Admin"],"summary":"Get HikariCP connection pool stats","operationId":"getPool","responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ConnectionPoolResponse"}}}}}}},"/admin/audit":{"get":{"tags":["Audit Log"],"summary":"Search audit log entries with pagination","operationId":"getAuditLog","parameters":[{"name":"username","in":"query","required":false,"schema":{"type":"string"}},{"name":"category","in":"query","required":false,"schema":{"type":"string"}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","default":"timestamp"}},{"name":"order","in":"query","required":false,"schema":{"type":"string","default":"desc"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":0}},{"name":"size","in":"query","required":false,"schema":{"type":"integer","format":"int32","default":25}}],"responses":{"200":{"description":"OK","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AuditLogPageResponse"}}}}}}},"/admin/opensearch/indices/{name}":{"delete":{"tags":["OpenSearch Admin"],"summary":"Delete an OpenSearch index","operationId":"deleteIndex","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}}},"components":{"schemas":{"UpdateUserRequest":{"type":"object","properties":{"displayName":{"type":"string"},"email":{"type":"string"}}},"DatabaseThresholdsRequest":{"type":"object","description":"Database monitoring thresholds","properties":{"connectionPoolWarning":{"type":"integer","format":"int32","description":"Connection pool usage warning threshold (percentage)","maximum":100,"minimum":0},"connectionPoolCritical":{"type":"integer","format":"int32","description":"Connection pool usage critical threshold (percentage)","maximum":100,"minimum":0},"queryDurationWarning":{"type":"number","format":"double","description":"Query duration warning threshold (seconds)"},"queryDurationCritical":{"type":"number","format":"double","description":"Query duration critical threshold (seconds)"}}},"OpenSearchThresholdsRequest":{"type":"object","description":"OpenSearch monitoring thresholds","properties":{"clusterHealthWarning":{"type":"string","description":"Cluster health warning threshold (GREEN, YELLOW, RED)","minLength":1},"clusterHealthCritical":{"type":"string","description":"Cluster health critical threshold (GREEN, YELLOW, RED)","minLength":1},"queueDepthWarning":{"type":"integer","format":"int32","description":"Queue depth warning threshold","minimum":0},"queueDepthCritical":{"type":"integer","format":"int32","description":"Queue depth critical threshold","minimum":0},"jvmHeapWarning":{"type":"integer","format":"int32","description":"JVM heap usage warning threshold (percentage)","maximum":100,"minimum":0},"jvmHeapCritical":{"type":"integer","format":"int32","description":"JVM heap usage critical threshold (percentage)","maximum":100,"minimum":0},"failedDocsWarning":{"type":"integer","format":"int32","description":"Failed document count warning threshold","minimum":0},"failedDocsCritical":{"type":"integer","format":"int32","description":"Failed document count critical threshold","minimum":0}}},"ThresholdConfigRequest":{"type":"object","description":"Threshold configuration for admin monitoring","properties":{"database":{"$ref":"#/components/schemas/DatabaseThresholdsRequest"},"opensearch":{"$ref":"#/components/schemas/OpenSearchThresholdsRequest"}},"required":["database","opensearch"]},"DatabaseThresholds":{"type":"object","properties":{"connectionPoolWarning":{"type":"integer","format":"int32"},"connectionPoolCritical":{"type":"integer","format":"int32"},"queryDurationWarning":{"type":"number","format":"double"},"queryDurationCritical":{"type":"number","format":"double"}}},"OpenSearchThresholds":{"type":"object","properties":{"clusterHealthWarning":{"type":"string"},"clusterHealthCritical":{"type":"string"},"queueDepthWarning":{"type":"integer","format":"int32"},"queueDepthCritical":{"type":"integer","format":"int32"},"jvmHeapWarning":{"type":"integer","format":"int32"},"jvmHeapCritical":{"type":"integer","format":"int32"},"failedDocsWarning":{"type":"integer","format":"int32"},"failedDocsCritical":{"type":"integer","format":"int32"}}},"ThresholdConfig":{"type":"object","properties":{"database":{"$ref":"#/components/schemas/DatabaseThresholds"},"opensearch":{"$ref":"#/components/schemas/OpenSearchThresholds"}}},"UpdateRoleRequest":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"scope":{"type":"string"}}},"OidcAdminConfigRequest":{"type":"object","description":"OIDC configuration update request","properties":{"enabled":{"type":"boolean"},"issuerUri":{"type":"string"},"clientId":{"type":"string"},"clientSecret":{"type":"string"},"rolesClaim":{"type":"string"},"defaultRoles":{"type":"array","items":{"type":"string"}},"autoSignup":{"type":"boolean"},"displayNameClaim":{"type":"string"}}},"ErrorResponse":{"type":"object","description":"Error response","properties":{"message":{"type":"string"}},"required":["message"]},"OidcAdminConfigResponse":{"type":"object","description":"OIDC configuration for admin management","properties":{"configured":{"type":"boolean"},"enabled":{"type":"boolean"},"issuerUri":{"type":"string"},"clientId":{"type":"string"},"clientSecretSet":{"type":"boolean"},"rolesClaim":{"type":"string"},"defaultRoles":{"type":"array","items":{"type":"string"}},"autoSignup":{"type":"boolean"},"displayNameClaim":{"type":"string"}}},"UpdateGroupRequest":{"type":"object","properties":{"name":{"type":"string"},"parentGroupId":{"type":"string","format":"uuid"}}},"SearchRequest":{"type":"object","properties":{"status":{"type":"string"},"timeFrom":{"type":"string","format":"date-time"},"timeTo":{"type":"string","format":"date-time"},"durationMin":{"type":"integer","format":"int64"},"durationMax":{"type":"integer","format":"int64"},"correlationId":{"type":"string"},"text":{"type":"string"},"textInBody":{"type":"string"},"textInHeaders":{"type":"string"},"textInErrors":{"type":"string"},"routeId":{"type":"string"},"agentId":{"type":"string"},"processorType":{"type":"string"},"application":{"type":"string"},"agentIds":{"type":"array","items":{"type":"string"}},"offset":{"type":"integer","format":"int32"},"limit":{"type":"integer","format":"int32"},"sortField":{"type":"string"},"sortDir":{"type":"string"}}},"ExecutionSummary":{"type":"object","properties":{"executionId":{"type":"string"},"routeId":{"type":"string"},"agentId":{"type":"string"},"applicationName":{"type":"string"},"status":{"type":"string"},"startTime":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"date-time"},"durationMs":{"type":"integer","format":"int64"},"correlationId":{"type":"string"},"errorMessage":{"type":"string"},"diagramContentHash":{"type":"string"}},"required":["agentId","applicationName","correlationId","diagramContentHash","durationMs","endTime","errorMessage","executionId","routeId","startTime","status"]},"SearchResultExecutionSummary":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ExecutionSummary"}},"total":{"type":"integer","format":"int64"},"offset":{"type":"integer","format":"int32"},"limit":{"type":"integer","format":"int32"}},"required":["data","limit","offset","total"]},"RefreshRequest":{"type":"object","properties":{"refreshToken":{"type":"string"}}},"AuthTokenResponse":{"type":"object","description":"JWT token pair","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"displayName":{"type":"string"},"idToken":{"type":"string","description":"OIDC id_token for end-session logout (only present after OIDC login)"}},"required":["accessToken","displayName","refreshToken"]},"CallbackRequest":{"type":"object","properties":{"code":{"type":"string"},"redirectUri":{"type":"string"}}},"LoginRequest":{"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string"}}},"AgentRefreshRequest":{"type":"object","description":"Agent token refresh request","properties":{"refreshToken":{"type":"string"}},"required":["refreshToken"]},"AgentRefreshResponse":{"type":"object","description":"Refreshed access and refresh tokens","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"}},"required":["accessToken","refreshToken"]},"CommandRequest":{"type":"object","description":"Command to send to agent(s)","properties":{"type":{"type":"string","description":"Command type: config-update, deep-trace, or replay"},"payload":{"type":"object","description":"Command payload JSON"}},"required":["type"]},"CommandSingleResponse":{"type":"object","description":"Result of sending a command to a single agent","properties":{"commandId":{"type":"string"},"status":{"type":"string"}},"required":["commandId","status"]},"AgentRegistrationRequest":{"type":"object","description":"Agent registration payload","properties":{"agentId":{"type":"string"},"name":{"type":"string"},"version":{"type":"string"},"routeIds":{"type":"array","items":{"type":"string"}},"capabilities":{"type":"object","additionalProperties":{"type":"object"}},"application":{"type":"string","default":"default"}},"required":["agentId","name"]},"AgentRegistrationResponse":{"type":"object","description":"Agent registration result with JWT tokens and SSE endpoint","properties":{"agentId":{"type":"string"},"sseEndpoint":{"type":"string"},"heartbeatIntervalMs":{"type":"integer","format":"int64"},"serverPublicKey":{"type":"string"},"accessToken":{"type":"string"},"refreshToken":{"type":"string"}},"required":["accessToken","agentId","refreshToken","serverPublicKey","sseEndpoint"]},"CommandBroadcastResponse":{"type":"object","description":"Result of broadcasting a command to multiple agents","properties":{"commandIds":{"type":"array","items":{"type":"string"}},"targetCount":{"type":"integer","format":"int32"}},"required":["commandIds"]},"CreateUserRequest":{"type":"object","properties":{"username":{"type":"string"},"displayName":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"}}},"GroupSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"}}},"RoleSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"system":{"type":"boolean"},"source":{"type":"string"}}},"UserDetail":{"type":"object","properties":{"userId":{"type":"string"},"provider":{"type":"string"},"email":{"type":"string"},"displayName":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"directRoles":{"type":"array","items":{"$ref":"#/components/schemas/RoleSummary"}},"directGroups":{"type":"array","items":{"$ref":"#/components/schemas/GroupSummary"}},"effectiveRoles":{"type":"array","items":{"$ref":"#/components/schemas/RoleSummary"}},"effectiveGroups":{"type":"array","items":{"$ref":"#/components/schemas/GroupSummary"}}}},"SetPasswordRequest":{"type":"object","properties":{"password":{"type":"string","minLength":1}}},"CreateRoleRequest":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"scope":{"type":"string"}}},"OidcTestResult":{"type":"object","description":"OIDC provider connectivity test result","properties":{"status":{"type":"string"},"authorizationEndpoint":{"type":"string"}},"required":["authorizationEndpoint","status"]},"CreateGroupRequest":{"type":"object","properties":{"name":{"type":"string"},"parentGroupId":{"type":"string","format":"uuid"}}},"ExecutionStats":{"type":"object","properties":{"totalCount":{"type":"integer","format":"int64"},"failedCount":{"type":"integer","format":"int64"},"avgDurationMs":{"type":"integer","format":"int64"},"p99LatencyMs":{"type":"integer","format":"int64"},"activeCount":{"type":"integer","format":"int64"},"totalToday":{"type":"integer","format":"int64"},"prevTotalCount":{"type":"integer","format":"int64"},"prevFailedCount":{"type":"integer","format":"int64"},"prevAvgDurationMs":{"type":"integer","format":"int64"},"prevP99LatencyMs":{"type":"integer","format":"int64"}},"required":["activeCount","avgDurationMs","failedCount","p99LatencyMs","prevAvgDurationMs","prevFailedCount","prevP99LatencyMs","prevTotalCount","totalCount","totalToday"]},"StatsTimeseries":{"type":"object","properties":{"buckets":{"type":"array","items":{"$ref":"#/components/schemas/TimeseriesBucket"}}},"required":["buckets"]},"TimeseriesBucket":{"type":"object","properties":{"time":{"type":"string","format":"date-time"},"totalCount":{"type":"integer","format":"int64"},"failedCount":{"type":"integer","format":"int64"},"avgDurationMs":{"type":"integer","format":"int64"},"p99DurationMs":{"type":"integer","format":"int64"},"activeCount":{"type":"integer","format":"int64"}},"required":["activeCount","avgDurationMs","failedCount","p99DurationMs","time","totalCount"]},"RouteMetrics":{"type":"object","description":"Aggregated route performance metrics","properties":{"routeId":{"type":"string"},"appId":{"type":"string"},"exchangeCount":{"type":"integer","format":"int64"},"successRate":{"type":"number","format":"double"},"avgDurationMs":{"type":"number","format":"double"},"p99DurationMs":{"type":"number","format":"double"},"errorRate":{"type":"number","format":"double"},"throughputPerSec":{"type":"number","format":"double"},"sparkline":{"type":"array","items":{"type":"number","format":"double"}}},"required":["appId","avgDurationMs","errorRate","exchangeCount","p99DurationMs","routeId","sparkline","successRate","throughputPerSec"]},"ProcessorMetrics":{"type":"object","properties":{"processorId":{"type":"string"},"processorType":{"type":"string"},"routeId":{"type":"string"},"appId":{"type":"string"},"totalCount":{"type":"integer","format":"int64"},"failedCount":{"type":"integer","format":"int64"},"avgDurationMs":{"type":"number","format":"double"},"p99DurationMs":{"type":"number","format":"double"},"errorRate":{"type":"number","format":"double"}},"required":["appId","avgDurationMs","errorRate","failedCount","p99DurationMs","processorId","processorType","routeId","totalCount"]},"AgentSummary":{"type":"object","description":"Summary of an agent instance for sidebar display","properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"tps":{"type":"number","format":"double"}},"required":["id","name","status","tps"]},"AppCatalogEntry":{"type":"object","description":"Application catalog entry with routes and agents","properties":{"appId":{"type":"string"},"routes":{"type":"array","items":{"$ref":"#/components/schemas/RouteSummary"}},"agents":{"type":"array","items":{"$ref":"#/components/schemas/AgentSummary"}},"agentCount":{"type":"integer","format":"int32"},"health":{"type":"string"},"exchangeCount":{"type":"integer","format":"int64"}},"required":["agentCount","agents","appId","exchangeCount","health","routes"]},"RouteSummary":{"type":"object","description":"Summary of a route within an application","properties":{"routeId":{"type":"string"},"exchangeCount":{"type":"integer","format":"int64"},"lastSeen":{"type":"string","format":"date-time"}},"required":["exchangeCount","lastSeen","routeId"]},"ExecutionDetail":{"type":"object","properties":{"executionId":{"type":"string"},"routeId":{"type":"string"},"agentId":{"type":"string"},"applicationName":{"type":"string"},"status":{"type":"string"},"startTime":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"date-time"},"durationMs":{"type":"integer","format":"int64"},"correlationId":{"type":"string"},"exchangeId":{"type":"string"},"errorMessage":{"type":"string"},"errorStackTrace":{"type":"string"},"diagramContentHash":{"type":"string"},"processors":{"type":"array","items":{"$ref":"#/components/schemas/ProcessorNode"}}},"required":["agentId","applicationName","correlationId","diagramContentHash","durationMs","endTime","errorMessage","errorStackTrace","exchangeId","executionId","processors","routeId","startTime","status"]},"ProcessorNode":{"type":"object","properties":{"processorId":{"type":"string"},"processorType":{"type":"string"},"status":{"type":"string"},"startTime":{"type":"string","format":"date-time"},"endTime":{"type":"string","format":"date-time"},"durationMs":{"type":"integer","format":"int64"},"diagramNodeId":{"type":"string"},"errorMessage":{"type":"string"},"errorStackTrace":{"type":"string"},"children":{"type":"array","items":{"$ref":"#/components/schemas/ProcessorNode"}}},"required":["children","diagramNodeId","durationMs","endTime","errorMessage","errorStackTrace","processorId","processorType","startTime","status"]},"DiagramLayout":{"type":"object","properties":{"width":{"type":"number","format":"double"},"height":{"type":"number","format":"double"},"nodes":{"type":"array","items":{"$ref":"#/components/schemas/PositionedNode"}},"edges":{"type":"array","items":{"$ref":"#/components/schemas/PositionedEdge"}}}},"PositionedEdge":{"type":"object","properties":{"sourceId":{"type":"string"},"targetId":{"type":"string"},"label":{"type":"string"},"points":{"type":"array","items":{"type":"array","items":{"type":"number","format":"double"}}}}},"PositionedNode":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"},"x":{"type":"number","format":"double"},"y":{"type":"number","format":"double"},"width":{"type":"number","format":"double"},"height":{"type":"number","format":"double"}}},"OidcPublicConfigResponse":{"type":"object","description":"OIDC configuration for SPA login flow","properties":{"issuer":{"type":"string"},"clientId":{"type":"string"},"authorizationEndpoint":{"type":"string"},"endSessionEndpoint":{"type":"string","description":"Present if the provider supports RP-initiated logout"}},"required":["authorizationEndpoint","clientId","issuer"]},"AgentInstanceResponse":{"type":"object","description":"Agent instance summary with runtime metrics","properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"routeIds":{"type":"array","items":{"type":"string"}},"registeredAt":{"type":"string","format":"date-time"},"lastHeartbeat":{"type":"string","format":"date-time"},"version":{"type":"string"},"capabilities":{"type":"object","additionalProperties":{"type":"object"}},"tps":{"type":"number","format":"double"},"errorRate":{"type":"number","format":"double"},"activeRoutes":{"type":"integer","format":"int32"},"totalRoutes":{"type":"integer","format":"int32"},"uptimeSeconds":{"type":"integer","format":"int64"},"application":{"type":"string"}},"required":["activeRoutes","capabilities","errorRate","application","id","lastHeartbeat","name","registeredAt","routeIds","status","totalRoutes","tps","uptimeSeconds","version"]},"SseEmitter":{"type":"object","properties":{"timeout":{"type":"integer","format":"int64"}}},"AgentMetricsResponse":{"type":"object","properties":{"metrics":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/MetricBucket"}}}},"required":["metrics"]},"MetricBucket":{"type":"object","properties":{"time":{"type":"string","format":"date-time"},"value":{"type":"number","format":"double"}},"required":["time","value"]},"AgentEventResponse":{"type":"object","description":"Agent lifecycle event","properties":{"id":{"type":"integer","format":"int64"},"agentId":{"type":"string"},"appId":{"type":"string"},"eventType":{"type":"string"},"detail":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["agentId","appId","detail","eventType","id","timestamp"]},"RoleDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string"},"scope":{"type":"string"},"system":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"assignedGroups":{"type":"array","items":{"$ref":"#/components/schemas/GroupSummary"}},"directUsers":{"type":"array","items":{"$ref":"#/components/schemas/UserSummary"}},"effectivePrincipals":{"type":"array","items":{"$ref":"#/components/schemas/UserSummary"}}}},"UserSummary":{"type":"object","properties":{"userId":{"type":"string"},"displayName":{"type":"string"},"provider":{"type":"string"}}},"RbacStats":{"type":"object","properties":{"userCount":{"type":"integer","format":"int32"},"activeUserCount":{"type":"integer","format":"int32"},"groupCount":{"type":"integer","format":"int32"},"maxGroupDepth":{"type":"integer","format":"int32"},"roleCount":{"type":"integer","format":"int32"}}},"OpenSearchStatusResponse":{"type":"object","description":"OpenSearch cluster status","properties":{"reachable":{"type":"boolean","description":"Whether the cluster is reachable"},"clusterHealth":{"type":"string","description":"Cluster health status (GREEN, YELLOW, RED)"},"version":{"type":"string","description":"OpenSearch version"},"nodeCount":{"type":"integer","format":"int32","description":"Number of nodes in the cluster"},"host":{"type":"string","description":"OpenSearch host"}}},"PipelineStatsResponse":{"type":"object","description":"Search indexing pipeline statistics","properties":{"queueDepth":{"type":"integer","format":"int32","description":"Current queue depth"},"maxQueueSize":{"type":"integer","format":"int32","description":"Maximum queue size"},"failedCount":{"type":"integer","format":"int64","description":"Number of failed indexing operations"},"indexedCount":{"type":"integer","format":"int64","description":"Number of successfully indexed documents"},"debounceMs":{"type":"integer","format":"int64","description":"Debounce interval in milliseconds"},"indexingRate":{"type":"number","format":"double","description":"Current indexing rate (docs/sec)"},"lastIndexedAt":{"type":"string","format":"date-time","description":"Timestamp of last indexed document"}}},"PerformanceResponse":{"type":"object","description":"OpenSearch performance metrics","properties":{"queryCacheHitRate":{"type":"number","format":"double","description":"Query cache hit rate (0.0-1.0)"},"requestCacheHitRate":{"type":"number","format":"double","description":"Request cache hit rate (0.0-1.0)"},"searchLatencyMs":{"type":"number","format":"double","description":"Average search latency in milliseconds"},"indexingLatencyMs":{"type":"number","format":"double","description":"Average indexing latency in milliseconds"},"jvmHeapUsedBytes":{"type":"integer","format":"int64","description":"JVM heap used in bytes"},"jvmHeapMaxBytes":{"type":"integer","format":"int64","description":"JVM heap max in bytes"}}},"IndexInfoResponse":{"type":"object","description":"OpenSearch index information","properties":{"name":{"type":"string","description":"Index name"},"docCount":{"type":"integer","format":"int64","description":"Document count"},"size":{"type":"string","description":"Human-readable index size"},"sizeBytes":{"type":"integer","format":"int64","description":"Index size in bytes"},"health":{"type":"string","description":"Index health status"},"primaryShards":{"type":"integer","format":"int32","description":"Number of primary shards"},"replicaShards":{"type":"integer","format":"int32","description":"Number of replica shards"}}},"IndicesPageResponse":{"type":"object","description":"Paginated list of OpenSearch indices","properties":{"indices":{"type":"array","description":"Index list for current page","items":{"$ref":"#/components/schemas/IndexInfoResponse"}},"totalIndices":{"type":"integer","format":"int64","description":"Total number of indices"},"totalDocs":{"type":"integer","format":"int64","description":"Total document count across all indices"},"totalSize":{"type":"string","description":"Human-readable total size"},"page":{"type":"integer","format":"int32","description":"Current page number (0-based)"},"pageSize":{"type":"integer","format":"int32","description":"Page size"},"totalPages":{"type":"integer","format":"int32","description":"Total number of pages"}}},"GroupDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"parentGroupId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"directRoles":{"type":"array","items":{"$ref":"#/components/schemas/RoleSummary"}},"effectiveRoles":{"type":"array","items":{"$ref":"#/components/schemas/RoleSummary"}},"members":{"type":"array","items":{"$ref":"#/components/schemas/UserSummary"}},"childGroups":{"type":"array","items":{"$ref":"#/components/schemas/GroupSummary"}}}},"TableSizeResponse":{"type":"object","description":"Table size and row count information","properties":{"tableName":{"type":"string","description":"Table name"},"rowCount":{"type":"integer","format":"int64","description":"Approximate row count"},"dataSize":{"type":"string","description":"Human-readable data size"},"indexSize":{"type":"string","description":"Human-readable index size"},"dataSizeBytes":{"type":"integer","format":"int64","description":"Data size in bytes"},"indexSizeBytes":{"type":"integer","format":"int64","description":"Index size in bytes"}}},"DatabaseStatusResponse":{"type":"object","description":"Database connection and version status","properties":{"connected":{"type":"boolean","description":"Whether the database is reachable"},"version":{"type":"string","description":"PostgreSQL version string"},"host":{"type":"string","description":"Database host"},"schema":{"type":"string","description":"Current schema search path"},"timescaleDb":{"type":"boolean","description":"Whether TimescaleDB extension is available"}}},"ActiveQueryResponse":{"type":"object","description":"Currently running database query","properties":{"pid":{"type":"integer","format":"int32","description":"Backend process ID"},"durationSeconds":{"type":"number","format":"double","description":"Query duration in seconds"},"state":{"type":"string","description":"Backend state (active, idle, etc.)"},"query":{"type":"string","description":"SQL query text"}}},"ConnectionPoolResponse":{"type":"object","description":"HikariCP connection pool statistics","properties":{"activeConnections":{"type":"integer","format":"int32","description":"Number of currently active connections"},"idleConnections":{"type":"integer","format":"int32","description":"Number of idle connections"},"pendingThreads":{"type":"integer","format":"int32","description":"Number of threads waiting for a connection"},"maxWaitMs":{"type":"integer","format":"int64","description":"Maximum wait time in milliseconds"},"maxPoolSize":{"type":"integer","format":"int32","description":"Maximum pool size"}}},"AuditLogPageResponse":{"type":"object","description":"Paginated audit log entries","properties":{"items":{"type":"array","description":"Audit log entries","items":{"$ref":"#/components/schemas/AuditRecord"}},"totalCount":{"type":"integer","format":"int64","description":"Total number of matching entries"},"page":{"type":"integer","format":"int32","description":"Current page number (0-based)"},"pageSize":{"type":"integer","format":"int32","description":"Page size"},"totalPages":{"type":"integer","format":"int32","description":"Total number of pages"}}},"AuditRecord":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"timestamp":{"type":"string","format":"date-time"},"username":{"type":"string"},"action":{"type":"string"},"category":{"type":"string","enum":["INFRA","AUTH","USER_MGMT","CONFIG","RBAC"]},"target":{"type":"string"},"detail":{"type":"object","additionalProperties":{"type":"object"}},"result":{"type":"string","enum":["SUCCESS","FAILURE"]},"ipAddress":{"type":"string"},"userAgent":{"type":"string"}}}},"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}}} \ No newline at end of file diff --git a/ui/src/api/queries/agents.ts b/ui/src/api/queries/agents.ts index 84dbaa28..56763c41 100644 --- a/ui/src/api/queries/agents.ts +++ b/ui/src/api/queries/agents.ts @@ -3,12 +3,12 @@ import { api } from '../client'; import { config } from '../../config'; import { useAuthStore } from '../../auth/auth-store'; -export function useAgents(status?: string, group?: string) { +export function useAgents(status?: string, application?: string) { return useQuery({ - queryKey: ['agents', status, group], + queryKey: ['agents', status, application], queryFn: async () => { const { data, error } = await api.GET('/agents', { - params: { query: { ...(status ? { status } : {}), ...(group ? { group } : {}) } }, + params: { query: { ...(status ? { status } : {}), ...(application ? { application } : {}) } }, }); if (error) throw new Error('Failed to load agents'); return data!; diff --git a/ui/src/api/queries/diagrams.ts b/ui/src/api/queries/diagrams.ts index c9a35163..60dd8ec5 100644 --- a/ui/src/api/queries/diagrams.ts +++ b/ui/src/api/queries/diagrams.ts @@ -28,7 +28,7 @@ export function useDiagramByRoute(application: string | undefined, routeId: stri queryKey: ['diagrams', 'byRoute', application, routeId], queryFn: async () => { const { data, error } = await api.GET('/diagrams', { - params: { query: { group: application!, routeId: routeId! } }, + params: { query: { application: application!, routeId: routeId! } }, }); if (error) throw new Error('Failed to load diagram for route'); return data!; diff --git a/ui/src/api/schema.d.ts b/ui/src/api/schema.d.ts index a9440b73..3b3f33a8 100644 --- a/ui/src/api/schema.d.ts +++ b/ui/src/api/schema.d.ts @@ -625,10 +625,10 @@ export interface paths { cookie?: never; }; /** - * Find diagram by application group and route ID - * @description Resolves group to agent IDs and finds the latest diagram for the route + * Find diagram by application and route ID + * @description Resolves application to agent IDs and finds the latest diagram for the route */ - get: operations["findByGroupAndRoute"]; + get: operations["findByApplicationAndRoute"]; put?: never; post?: never; delete?: never; @@ -683,7 +683,7 @@ export interface paths { }; /** * List all agents - * @description Returns all registered agents with runtime metrics, optionally filtered by status and/or group + * @description Returns all registered agents with runtime metrics, optionally filtered by status and/or application */ get: operations["listAgents"]; put?: never; @@ -1158,7 +1158,7 @@ export interface components { agentId: string; name: string; /** @default default */ - group: string; + application: string; version?: string; routeIds?: string[]; capabilities?: { @@ -1384,7 +1384,7 @@ export interface components { AgentInstanceResponse: { id: string; name: string; - group: string; + application: string; status: string; routeIds: string[]; /** Format: date-time */ @@ -3133,10 +3133,10 @@ export interface operations { }; }; }; - findByGroupAndRoute: { + findByApplicationAndRoute: { parameters: { query: { - group: string; + application: string; routeId: string; }; header?: never; @@ -3154,7 +3154,7 @@ export interface operations { "*/*": components["schemas"]["DiagramLayout"]; }; }; - /** @description No diagram found for the given group and route */ + /** @description No diagram found for the given application and route */ 404: { headers: { [name: string]: unknown; @@ -3239,7 +3239,7 @@ export interface operations { parameters: { query?: { status?: string; - group?: string; + application?: string; }; header?: never; path?: never; diff --git a/ui/src/pages/AgentHealth/AgentHealth.tsx b/ui/src/pages/AgentHealth/AgentHealth.tsx index de3b217d..dbe4f8fc 100644 --- a/ui/src/pages/AgentHealth/AgentHealth.tsx +++ b/ui/src/pages/AgentHealth/AgentHealth.tsx @@ -63,7 +63,7 @@ function AgentOverviewContent({ agent }: { agent: any }) {
Application
-
{agent.group ?? '—'}
+
{agent.application ?? '—'}
Version
@@ -175,7 +175,7 @@ export default function AgentHealth() { const agentsByApp = useMemo(() => { const map: Record = {}; (agents || []).forEach((a: any) => { - const g = a.group; + const g = a.application; if (!map[g]) map[g] = []; map[g].push(a); }); @@ -185,7 +185,7 @@ export default function AgentHealth() { const liveCount = (agents || []).filter((a: any) => a.status === 'LIVE').length; const staleCount = (agents || []).filter((a: any) => a.status === 'STALE').length; const deadCount = (agents || []).filter((a: any) => a.status === 'DEAD').length; - const uniqueApps = new Set((agents || []).map((a: any) => a.group)).size; + const uniqueApps = new Set((agents || []).map((a: any) => a.application)).size; const activeRoutes = (agents || []).filter((a: any) => a.status === 'LIVE').reduce((sum: number, a: any) => sum + (a.activeRoutes || 0), 0); const totalTps = (agents || []).filter((a: any) => a.status === 'LIVE').reduce((sum: number, a: any) => sum + (a.tps || 0), 0);