diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ad37ae8..3eea626 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -172,7 +172,10 @@ jobs:
- name: Check for SQL string interpolation
run: |
- if grep -rn "format!.*SELECT\|format!.*INSERT\|format!.*UPDATE\|format!.*DELETE" crates/ services/ --include="*.rs"; then
+ # Match DML patterns only (SELECT FROM, INSERT INTO, UPDATE SET, DELETE FROM).
+ # GRANT/REVOKE DDL in partition.rs uses format! with internally-generated
+ # partition names (not user input) and is intentionally excluded.
+ if grep -rn "format!.*SELECT.*FROM\|format!.*INSERT.*INTO\|format!.*UPDATE.*SET\|format!.*DELETE FROM" crates/ services/ --include="*.rs"; then
echo "::error::SQL string interpolation found - use parameterized queries"
exit 1
fi
diff --git a/services/approval-ui/src/App.tsx b/services/approval-ui/src/App.tsx
index 75ed44d..76f6dea 100644
--- a/services/approval-ui/src/App.tsx
+++ b/services/approval-ui/src/App.tsx
@@ -1,93 +1,165 @@
-import { Router, Link } from './Router';
-import { ApprovalPage, AgentsPage, AgentActivityPage, DashboardPage } from './pages';
+import { Router, Link } from "./Router";
+import {
+ ApprovalPage,
+ AgentsPage,
+ AgentActivityPage,
+ DashboardPage,
+} from "./pages";
const routes = [
- { pattern: '/', component: HomePage },
- { pattern: '/approve/:grant_id', component: ApprovalPage },
- { pattern: '/agents', component: AgentsPage },
- { pattern: '/agents/:agent_id/activity', component: AgentActivityPage },
- { pattern: '/dashboard', component: DashboardPage },
+ { pattern: "/", component: HomePage },
+ { pattern: "/approve/:grant_id", component: ApprovalPage },
+ { pattern: "/agents", component: AgentsPage },
+ { pattern: "/agents/:agent_id/activity", component: AgentActivityPage },
+ { pattern: "/dashboard", component: DashboardPage },
];
function HomePage() {
- return (
-
-
- {/* Logo mark */}
-
+ return (
+
+
+ {/* Logo mark */}
+
-
- PERMISSION CONTROL INTERFACE
-
-
+
+ PERMISSION CONTROL INTERFACE
+
+
-
-
-
- VIEW AGENTS
-
-
-
- DASHBOARD
-
-
-
+
+
+
+ VIEW AGENTS
+
+
+
+ DASHBOARD
+
+
+
- {/* Grid decoration */}
-
-
- );
+ {/* Grid decoration */}
+
+
+ );
}
function NotFound() {
- return (
-
-
404
-
SECTOR NOT FOUND
-
- RETURN TO BASE
-
-
- );
+ return (
+
+
+ 404
+
+
+ SECTOR NOT FOUND
+
+
+ RETURN TO BASE
+
+
+ );
}
function App() {
- return (
-
-
-
- );
+ return (
+
+
+
+ );
}
export default App;
diff --git a/services/approval-ui/src/Router.tsx b/services/approval-ui/src/Router.tsx
index 8d52a3b..025c9c9 100644
--- a/services/approval-ui/src/Router.tsx
+++ b/services/approval-ui/src/Router.tsx
@@ -1,146 +1,143 @@
// Simple client-side router for the approval UI
-import { useState, useEffect, createContext, useContext } from 'react';
+import { useState, useEffect, createContext, useContext } from "react";
interface RouteParams {
- [key: string]: string;
+ [key: string]: string;
}
interface RouterContextValue {
- path: string;
- params: RouteParams;
- navigate: (path: string) => void;
+ path: string;
+ params: RouteParams;
+ navigate: (path: string) => void;
}
const RouterContext = createContext({
- path: '/',
- params: {},
- navigate: () => {},
+ path: "/",
+ params: {},
+ navigate: () => {},
});
/** Hook to access router context */
export function useRouter() {
- return useContext(RouterContext);
+ return useContext(RouterContext);
}
/** Hook to get route parameters */
export function useParams(): T {
- const { params } = useRouter();
- return params as T;
+ const { params } = useRouter();
+ return params as T;
}
/** Parse route pattern and extract params */
-function matchRoute(
- pattern: string,
- path: string
-): RouteParams | null {
- const patternParts = pattern.split('/').filter(Boolean);
- const pathParts = path.split('/').filter(Boolean);
-
- if (patternParts.length !== pathParts.length) {
- return null;
- }
-
- const params: RouteParams = {};
-
- for (let i = 0; i < patternParts.length; i++) {
- const patternPart = patternParts[i]!;
- const pathPart = pathParts[i]!;
-
- if (patternPart.startsWith(':')) {
- // This is a parameter
- const paramName = patternPart.slice(1);
- params[paramName] = decodeURIComponent(pathPart);
- } else if (patternPart !== pathPart) {
- // Static part doesn't match
- return null;
- }
- }
-
- return params;
+function matchRoute(pattern: string, path: string): RouteParams | null {
+ const patternParts = pattern.split("/").filter(Boolean);
+ const pathParts = path.split("/").filter(Boolean);
+
+ if (patternParts.length !== pathParts.length) {
+ return null;
+ }
+
+ const params: RouteParams = {};
+
+ for (let i = 0; i < patternParts.length; i++) {
+ const patternPart = patternParts[i]!;
+ const pathPart = pathParts[i]!;
+
+ if (patternPart.startsWith(":")) {
+ // This is a parameter
+ const paramName = patternPart.slice(1);
+ params[paramName] = decodeURIComponent(pathPart);
+ } else if (patternPart !== pathPart) {
+ // Static part doesn't match
+ return null;
+ }
+ }
+
+ return params;
}
interface Route {
- pattern: string;
- component: React.ComponentType;
+ pattern: string;
+ component: React.ComponentType;
}
interface RouterProps {
- routes: Route[];
- notFound?: React.ComponentType;
+ routes: Route[];
+ notFound?: React.ComponentType;
}
/** Router component */
export function Router({ routes, notFound: NotFound }: RouterProps) {
- const [path, setPath] = useState(window.location.pathname);
-
- useEffect(() => {
- const handlePopState = () => {
- setPath(window.location.pathname);
- };
-
- window.addEventListener('popstate', handlePopState);
- return () => window.removeEventListener('popstate', handlePopState);
- }, []);
-
- const navigate = (newPath: string) => {
- window.history.pushState({}, '', newPath);
- setPath(newPath);
- };
-
- // Find matching route
- let matchedRoute: Route | null = null;
- let params: RouteParams = {};
-
- for (const route of routes) {
- const match = matchRoute(route.pattern, path);
- if (match !== null) {
- matchedRoute = route;
- params = match;
- break;
- }
- }
-
- const contextValue: RouterContextValue = {
- path,
- params,
- navigate,
- };
-
- return (
-
- {matchedRoute ? (
-
- ) : NotFound ? (
-
- ) : (
-
-
404 - Page Not Found
-
The page you're looking for doesn't exist.
-
- )}
-
- );
+ const [path, setPath] = useState(window.location.pathname);
+
+ useEffect(() => {
+ const handlePopState = () => {
+ setPath(window.location.pathname);
+ };
+
+ window.addEventListener("popstate", handlePopState);
+ return () => window.removeEventListener("popstate", handlePopState);
+ }, []);
+
+ const navigate = (newPath: string) => {
+ window.history.pushState({}, "", newPath);
+ setPath(newPath);
+ };
+
+ // Find matching route
+ let matchedRoute: Route | null = null;
+ let params: RouteParams = {};
+
+ for (const route of routes) {
+ const match = matchRoute(route.pattern, path);
+ if (match !== null) {
+ matchedRoute = route;
+ params = match;
+ break;
+ }
+ }
+
+ const contextValue: RouterContextValue = {
+ path,
+ params,
+ navigate,
+ };
+
+ return (
+
+ {matchedRoute ? (
+
+ ) : NotFound ? (
+
+ ) : (
+
+
404 - Page Not Found
+
The page you're looking for doesn't exist.
+
+ )}
+
+ );
}
/** Link component for navigation */
interface LinkProps extends React.AnchorHTMLAttributes {
- to: string;
- children: React.ReactNode;
+ to: string;
+ children: React.ReactNode;
}
export function Link({ to, children, onClick, ...props }: LinkProps) {
- const { navigate } = useRouter();
-
- const handleClick = (e: React.MouseEvent) => {
- e.preventDefault();
- if (onClick) onClick(e);
- navigate(to);
- };
-
- return (
-
- {children}
-
- );
+ const { navigate } = useRouter();
+
+ const handleClick = (e: React.MouseEvent) => {
+ e.preventDefault();
+ if (onClick) onClick(e);
+ navigate(to);
+ };
+
+ return (
+
+ {children}
+
+ );
}
diff --git a/services/approval-ui/src/api.ts b/services/approval-ui/src/api.ts
index a2e25c8..5f36f45 100644
--- a/services/approval-ui/src/api.ts
+++ b/services/approval-ui/src/api.ts
@@ -1,182 +1,185 @@
// AgentAuth Registry API Client with CSRF Protection
import type {
- GrantRequest,
- AgentSummary,
- AgentDetails,
- AuditEvent,
- ApprovalAssertion,
- ApiError,
-} from './types';
+ GrantRequest,
+ AgentSummary,
+ AgentDetails,
+ AuditEvent,
+ ApprovalAssertion,
+ ApiError,
+} from "./types";
-const REGISTRY_URL = 'http://localhost:8080';
+const REGISTRY_URL = "http://localhost:8080";
/** CSRF token stored in memory and synced with cookie */
let csrfToken: string | null = null;
/** Generate a random CSRF token */
function generateCsrfToken(): string {
- const array = new Uint8Array(32);
- crypto.getRandomValues(array);
- return Array.from(array, (b) => b.toString(16).padStart(2, '0')).join('');
+ const array = new Uint8Array(32);
+ crypto.getRandomValues(array);
+ return Array.from(array, (b) => b.toString(16).padStart(2, "0")).join("");
}
/** Get or create CSRF token */
export function getCsrfToken(): string {
- if (!csrfToken) {
- // Try to read from cookie first
- const cookies = document.cookie.split(';');
- for (const cookie of cookies) {
- const [name, value] = cookie.trim().split('=');
- if (name === 'csrf_token' && value) {
- csrfToken = value;
- break;
- }
- }
- // Generate new token if not found
- if (!csrfToken) {
- csrfToken = generateCsrfToken();
- // Set as SameSite=Strict cookie
- document.cookie = `csrf_token=${csrfToken}; SameSite=Strict; Secure; Path=/`;
- }
- }
- return csrfToken;
+ if (!csrfToken) {
+ // Try to read from cookie first
+ const cookies = document.cookie.split(";");
+ for (const cookie of cookies) {
+ const [name, value] = cookie.trim().split("=");
+ if (name === "csrf_token" && value) {
+ csrfToken = value;
+ break;
+ }
+ }
+ // Generate new token if not found
+ if (!csrfToken) {
+ csrfToken = generateCsrfToken();
+ // Set as SameSite=Strict cookie
+ document.cookie = `csrf_token=${csrfToken}; SameSite=Strict; Secure; Path=/`;
+ }
+ }
+ return csrfToken;
}
/** Custom error class for API errors */
export class RegistryError extends Error {
- code: string;
- details?: Record;
-
- constructor(apiError: ApiError) {
- super(apiError.error);
- this.name = 'RegistryError';
- this.code = apiError.code;
- this.details = apiError.details;
- }
+ code: string;
+ details?: Record;
+
+ constructor(apiError: ApiError) {
+ super(apiError.error);
+ this.name = "RegistryError";
+ this.code = apiError.code;
+ this.details = apiError.details;
+ }
}
/** Make an authenticated request to the registry */
-async function request(
- path: string,
- options: RequestInit = {}
-): Promise {
- const url = `${REGISTRY_URL}${path}`;
- const headers = new Headers(options.headers);
-
- // Add CSRF token for state-changing requests
- if (options.method && ['POST', 'PUT', 'DELETE', 'PATCH'].includes(options.method)) {
- headers.set('X-CSRF-Token', getCsrfToken());
- }
-
- // Add content type for JSON bodies
- if (options.body && typeof options.body === 'string') {
- headers.set('Content-Type', 'application/json');
- }
-
- // Add credentials for cookie handling
- const response = await fetch(url, {
- ...options,
- headers,
- credentials: 'include',
- });
-
- if (!response.ok) {
- let apiError: ApiError;
- try {
- apiError = (await response.json()) as ApiError;
- } catch {
- apiError = {
- error: `Request failed with status ${response.status}`,
- code: 'REQUEST_FAILED',
- };
- }
- throw new RegistryError(apiError);
- }
-
- // Handle empty responses
- const text = await response.text();
- if (!text) {
- return {} as T;
- }
-
- return JSON.parse(text) as T;
+async function request(path: string, options: RequestInit = {}): Promise {
+ const url = `${REGISTRY_URL}${path}`;
+ const headers = new Headers(options.headers);
+
+ // Add CSRF token for state-changing requests
+ if (
+ options.method &&
+ ["POST", "PUT", "DELETE", "PATCH"].includes(options.method)
+ ) {
+ headers.set("X-CSRF-Token", getCsrfToken());
+ }
+
+ // Add content type for JSON bodies
+ if (options.body && typeof options.body === "string") {
+ headers.set("Content-Type", "application/json");
+ }
+
+ // Add credentials for cookie handling
+ const response = await fetch(url, {
+ ...options,
+ headers,
+ credentials: "include",
+ });
+
+ if (!response.ok) {
+ let apiError: ApiError;
+ try {
+ apiError = (await response.json()) as ApiError;
+ } catch {
+ apiError = {
+ error: `Request failed with status ${response.status}`,
+ code: "REQUEST_FAILED",
+ };
+ }
+ throw new RegistryError(apiError);
+ }
+
+ // Handle empty responses
+ const text = await response.text();
+ if (!text) {
+ return {} as T;
+ }
+
+ return JSON.parse(text) as T;
}
/** Fetch a grant request by ID */
export async function getGrantRequest(grantId: string): Promise {
- return request(`/v1/grants/${grantId}`);
+ return request(`/v1/grants/${grantId}`);
}
/** Submit approval for a grant */
export async function approveGrant(
- grantId: string,
- approvedBy: string,
- approvalNonce: string,
- approvalSignature: string
+ grantId: string,
+ approvedBy: string,
+ approvalNonce: string,
+ approvalSignature: string,
): Promise {
- await request(`/v1/grants/${grantId}/approve`, {
- method: 'POST',
- body: JSON.stringify({
- approved_by: approvedBy,
- approval_nonce: approvalNonce,
- approval_signature: approvalSignature,
- }),
- });
+ await request(`/v1/grants/${grantId}/approve`, {
+ method: "POST",
+ body: JSON.stringify({
+ approved_by: approvedBy,
+ approval_nonce: approvalNonce,
+ approval_signature: approvalSignature,
+ }),
+ });
}
/** Deny a grant request */
-export async function denyGrant(grantId: string, reason?: string): Promise {
- await request(`/v1/grants/${grantId}/deny`, {
- method: 'POST',
- body: JSON.stringify({ reason }),
- });
+export async function denyGrant(
+ grantId: string,
+ reason?: string,
+): Promise {
+ await request(`/v1/grants/${grantId}/deny`, {
+ method: "POST",
+ body: JSON.stringify({ reason }),
+ });
}
/** List all agents for the current human principal */
export async function listAgents(): Promise {
- return request('/v1/agents');
+ return request("/v1/agents");
}
/** Get agent details */
export async function getAgentDetails(agentId: string): Promise {
- return request(`/v1/agents/${agentId}`);
+ return request(`/v1/agents/${agentId}`);
}
/** Get audit events for an agent */
export async function getAgentActivity(
- agentId: string,
- limit = 50,
- offset = 0
+ agentId: string,
+ limit = 50,
+ offset = 0,
): Promise {
- return request(
- `/v1/audit/${agentId}?limit=${limit}&offset=${offset}`
- );
+ return request(
+ `/v1/audit/${agentId}?limit=${limit}&offset=${offset}`,
+ );
}
/** Revoke an agent */
export async function revokeAgent(agentId: string): Promise {
- await request(`/v1/agents/${agentId}`, {
- method: 'DELETE',
- });
+ await request(`/v1/agents/${agentId}`, {
+ method: "DELETE",
+ });
}
/** Revoke a specific grant */
export async function revokeGrant(grantId: string): Promise {
- await request(`/v1/grants/${grantId}/revoke`, {
- method: 'POST',
- });
+ await request(`/v1/grants/${grantId}/revoke`, {
+ method: "POST",
+ });
}
/** Check if registry is reachable */
export async function checkHealth(): Promise {
- try {
- const response = await fetch(`${REGISTRY_URL}/health/ready`, {
- method: 'GET',
- credentials: 'include',
- });
- return response.ok;
- } catch {
- return false;
- }
+ try {
+ const response = await fetch(`${REGISTRY_URL}/health/ready`, {
+ method: "GET",
+ credentials: "include",
+ });
+ return response.ok;
+ } catch {
+ return false;
+ }
}
diff --git a/services/approval-ui/src/index.css b/services/approval-ui/src/index.css
index 6e8fa84..0bf9967 100644
--- a/services/approval-ui/src/index.css
+++ b/services/approval-ui/src/index.css
@@ -2,109 +2,151 @@
/* Base */
* {
- box-sizing: border-box;
- margin: 0;
- padding: 0;
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
}
body {
- background: #0a0a0f;
- color: #e8e8f0;
- font-family: 'DM Sans', 'Inter', system-ui, -apple-system, sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- line-height: 1.5;
+ background: #0a0a0f;
+ color: #e8e8f0;
+ font-family:
+ "DM Sans",
+ "Inter",
+ system-ui,
+ -apple-system,
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ line-height: 1.5;
}
/* Scanline overlay */
body::after {
- content: '';
- position: fixed;
- inset: 0;
- pointer-events: none;
- z-index: 9999;
- background: repeating-linear-gradient(
- 0deg,
- transparent,
- transparent 2px,
- rgba(0, 0, 0, 0.03) 2px,
- rgba(0, 0, 0, 0.03) 4px
- );
+ content: "";
+ position: fixed;
+ inset: 0;
+ pointer-events: none;
+ z-index: 9999;
+ background: repeating-linear-gradient(
+ 0deg,
+ transparent,
+ transparent 2px,
+ rgba(0, 0, 0, 0.03) 2px,
+ rgba(0, 0, 0, 0.03) 4px
+ );
}
/* Animations */
@keyframes pulse-glow {
- 0%, 100% { opacity: 0.4; }
- 50% { opacity: 1; }
+ 0%,
+ 100% {
+ opacity: 0.4;
+ }
+ 50% {
+ opacity: 1;
+ }
}
@keyframes fade-in {
- from { opacity: 0; transform: translateY(8px); }
- to { opacity: 1; transform: translateY(0); }
+ from {
+ opacity: 0;
+ transform: translateY(8px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
}
@keyframes slide-up {
- from { opacity: 0; transform: translateY(16px); }
- to { opacity: 1; transform: translateY(0); }
+ from {
+ opacity: 0;
+ transform: translateY(16px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
}
.animate-fade-in {
- animation: fade-in 0.3s ease-out both;
+ animation: fade-in 0.3s ease-out both;
}
.animate-slide-up {
- animation: slide-up 0.4s ease-out both;
+ animation: slide-up 0.4s ease-out both;
}
/* Stagger children animation */
.stagger-children > * {
- animation: fade-in 0.3s ease-out both;
-}
-.stagger-children > *:nth-child(1) { animation-delay: 0ms; }
-.stagger-children > *:nth-child(2) { animation-delay: 50ms; }
-.stagger-children > *:nth-child(3) { animation-delay: 100ms; }
-.stagger-children > *:nth-child(4) { animation-delay: 150ms; }
-.stagger-children > *:nth-child(5) { animation-delay: 200ms; }
-.stagger-children > *:nth-child(6) { animation-delay: 250ms; }
-.stagger-children > *:nth-child(7) { animation-delay: 300ms; }
-.stagger-children > *:nth-child(8) { animation-delay: 350ms; }
+ animation: fade-in 0.3s ease-out both;
+}
+.stagger-children > *:nth-child(1) {
+ animation-delay: 0ms;
+}
+.stagger-children > *:nth-child(2) {
+ animation-delay: 50ms;
+}
+.stagger-children > *:nth-child(3) {
+ animation-delay: 100ms;
+}
+.stagger-children > *:nth-child(4) {
+ animation-delay: 150ms;
+}
+.stagger-children > *:nth-child(5) {
+ animation-delay: 200ms;
+}
+.stagger-children > *:nth-child(6) {
+ animation-delay: 250ms;
+}
+.stagger-children > *:nth-child(7) {
+ animation-delay: 300ms;
+}
+.stagger-children > *:nth-child(8) {
+ animation-delay: 350ms;
+}
/* Skeleton loading */
@keyframes shimmer {
- 0% { background-position: -400px 0; }
- 100% { background-position: 400px 0; }
+ 0% {
+ background-position: -400px 0;
+ }
+ 100% {
+ background-position: 400px 0;
+ }
}
.skeleton {
- background: linear-gradient(90deg, #111118 25%, #1a1a24 50%, #111118 75%);
- background-size: 800px 100%;
- animation: shimmer 1.5s infinite linear;
- border-radius: 2px;
+ background: linear-gradient(90deg, #111118 25%, #1a1a24 50%, #111118 75%);
+ background-size: 800px 100%;
+ animation: shimmer 1.5s infinite linear;
+ border-radius: 2px;
}
/* Custom scrollbar */
::-webkit-scrollbar {
- width: 6px;
+ width: 6px;
}
::-webkit-scrollbar-track {
- background: #0a0a0f;
+ background: #0a0a0f;
}
::-webkit-scrollbar-thumb {
- background: #2a2a3a;
- border-radius: 3px;
+ background: #2a2a3a;
+ border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
- background: #3a3a4e;
+ background: #3a3a4e;
}
/* Focus ring */
:focus-visible {
- outline: 1px solid #f59e0b;
- outline-offset: 2px;
+ outline: 1px solid #f59e0b;
+ outline-offset: 2px;
}
/* Selection */
::selection {
- background: rgba(245, 158, 11, 0.3);
- color: #e8e8f0;
+ background: rgba(245, 158, 11, 0.3);
+ color: #e8e8f0;
}
diff --git a/services/approval-ui/src/index.html b/services/approval-ui/src/index.html
index 44ebc20..aac543e 100644
--- a/services/approval-ui/src/index.html
+++ b/services/approval-ui/src/index.html
@@ -1,51 +1,66 @@
-
+
-
-
-
- AgentAuth // Control
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ AgentAuth // Control
+
+
+
+
+
+
+
+
+
+
+
diff --git a/services/approval-ui/src/main.tsx b/services/approval-ui/src/main.tsx
index 964aeb4..abaa67c 100644
--- a/services/approval-ui/src/main.tsx
+++ b/services/approval-ui/src/main.tsx
@@ -1,10 +1,10 @@
-import React from 'react'
-import ReactDOM from 'react-dom/client'
-import App from './App'
-import './index.css'
+import React from "react";
+import ReactDOM from "react-dom/client";
+import App from "./App";
+import "./index.css";
-ReactDOM.createRoot(document.getElementById('root')!).render(
-
-
- ,
-)
+ReactDOM.createRoot(document.getElementById("root")!).render(
+
+
+ ,
+);
diff --git a/services/approval-ui/src/pages/AgentActivityPage.tsx b/services/approval-ui/src/pages/AgentActivityPage.tsx
index 8935dc6..af6d18e 100644
--- a/services/approval-ui/src/pages/AgentActivityPage.tsx
+++ b/services/approval-ui/src/pages/AgentActivityPage.tsx
@@ -1,422 +1,557 @@
-import { useState, useEffect } from 'react';
-import { useParams, Link, useRouter } from '../Router';
+import { useState, useEffect } from "react";
+import { useParams, Link, useRouter } from "../Router";
import {
- getAgentDetails,
- getAgentActivity,
- revokeAgent,
- revokeGrant,
- checkHealth,
-} from '../api';
-import { capabilityToHumanReadable } from '../utils/capabilities';
-import type { AgentDetails, AuditEvent, GrantSummary } from '../types';
+ getAgentDetails,
+ getAgentActivity,
+ revokeAgent,
+ revokeGrant,
+ checkHealth,
+} from "../api";
+import { capabilityToHumanReadable } from "../utils/capabilities";
+import type { AgentDetails, AuditEvent, GrantSummary } from "../types";
type PageState =
- | { type: 'loading' }
- | { type: 'error'; message: string; isOffline: boolean }
- | { type: 'loaded'; agent: AgentDetails; events: AuditEvent[] };
+ | { type: "loading" }
+ | { type: "error"; message: string; isOffline: boolean }
+ | { type: "loaded"; agent: AgentDetails; events: AuditEvent[] };
export function AgentActivityPage() {
- const { agent_id } = useParams<{ agent_id: string }>();
- const { navigate } = useRouter();
- const [state, setState] = useState({ type: 'loading' });
- const [showRevokeConfirm, setShowRevokeConfirm] = useState(false);
- const [revokeGrantId, setRevokeGrantId] = useState(null);
+ const { agent_id } = useParams<{ agent_id: string }>();
+ const { navigate } = useRouter();
+ const [state, setState] = useState({ type: "loading" });
+ const [showRevokeConfirm, setShowRevokeConfirm] = useState(false);
+ const [revokeGrantId, setRevokeGrantId] = useState(null);
- useEffect(() => {
- loadAgent();
- }, [agent_id]);
+ useEffect(() => {
+ loadAgent();
+ }, [agent_id]);
- async function loadAgent() {
- setState({ type: 'loading' });
- const isHealthy = await checkHealth();
- if (!isHealthy) {
- setState({
- type: 'error',
- message: 'Unable to establish connection with AgentAuth registry.',
- isOffline: true,
- });
- return;
- }
- try {
- const [agent, events] = await Promise.all([
- getAgentDetails(agent_id),
- getAgentActivity(agent_id),
- ]);
- setState({ type: 'loaded', agent, events });
- } catch (err) {
- setState({
- type: 'error',
- message: err instanceof Error ? err.message : 'Failed to load agent details',
- isOffline: false,
- });
- }
- }
+ async function loadAgent() {
+ setState({ type: "loading" });
+ const isHealthy = await checkHealth();
+ if (!isHealthy) {
+ setState({
+ type: "error",
+ message:
+ "Unable to establish connection with AgentAuth registry.",
+ isOffline: true,
+ });
+ return;
+ }
+ try {
+ const [agent, events] = await Promise.all([
+ getAgentDetails(agent_id),
+ getAgentActivity(agent_id),
+ ]);
+ setState({ type: "loaded", agent, events });
+ } catch (err) {
+ setState({
+ type: "error",
+ message:
+ err instanceof Error
+ ? err.message
+ : "Failed to load agent details",
+ isOffline: false,
+ });
+ }
+ }
- async function handleRevokeAgent() {
- try {
- await revokeAgent(agent_id);
- navigate('/agents');
- } catch (err) {
- setState({
- type: 'error',
- message: err instanceof Error ? err.message : 'Failed to revoke agent',
- isOffline: false,
- });
- }
- }
+ async function handleRevokeAgent() {
+ try {
+ await revokeAgent(agent_id);
+ navigate("/agents");
+ } catch (err) {
+ setState({
+ type: "error",
+ message:
+ err instanceof Error
+ ? err.message
+ : "Failed to revoke agent",
+ isOffline: false,
+ });
+ }
+ }
- async function handleRevokeGrant(grantId: string) {
- try {
- await revokeGrant(grantId);
- setRevokeGrantId(null);
- loadAgent();
- } catch (err) {
- setState({
- type: 'error',
- message: err instanceof Error ? err.message : 'Failed to revoke grant',
- isOffline: false,
- });
- }
- }
+ async function handleRevokeGrant(grantId: string) {
+ try {
+ await revokeGrant(grantId);
+ setRevokeGrantId(null);
+ loadAgent();
+ } catch (err) {
+ setState({
+ type: "error",
+ message:
+ err instanceof Error
+ ? err.message
+ : "Failed to revoke grant",
+ isOffline: false,
+ });
+ }
+ }
- const statusConfig = {
- active: { color: 'bg-green', text: 'text-green', label: 'ACTIVE' },
- suspended: { color: 'bg-amber', text: 'text-amber', label: 'SUSPENDED' },
- revoked: { color: 'bg-red', text: 'text-red', label: 'REVOKED' },
- };
+ const statusConfig = {
+ active: { color: "bg-green", text: "text-green", label: "ACTIVE" },
+ suspended: {
+ color: "bg-amber",
+ text: "text-amber",
+ label: "SUSPENDED",
+ },
+ revoked: { color: "bg-red", text: "text-red", label: "REVOKED" },
+ };
- return (
-
- {/* Top bar */}
-
-
-
-
-
AGENTAUTH
-
-
- AGENTS
- /
- DETAIL
-
-
-
+ return (
+
+ {/* Top bar */}
+
+
+
+
+
+ AGENTAUTH
+
+
+
+
+ AGENTS
+
+ /
+ DETAIL
+
+
+
-
- {/* Loading */}
- {state.type === 'loading' && (
-
- )}
+
+ {/* Loading */}
+ {state.type === "loading" && (
+
+ )}
- {/* Error */}
- {state.type === 'error' && (
-
-
-
-
-
-
- {state.isOffline ? 'CONNECTION LOST' : 'ERROR'}
-
-
{state.message}
-
-
-
-
-
- BACK
-
-
-
-
- )}
+ {/* Error */}
+ {state.type === "error" && (
+
+
+
+
+
+
+ {state.isOffline
+ ? "CONNECTION LOST"
+ : "ERROR"}
+
+
+ {state.message}
+
+
+
+
+
+
+ BACK
+
+
+
+
+ )}
- {/* Loaded */}
- {state.type === 'loaded' && (() => {
- const { agent, events } = state;
- const status = statusConfig[agent.status];
+ {/* Loaded */}
+ {state.type === "loaded" &&
+ (() => {
+ const { agent, events } = state;
+ const status = statusConfig[agent.status];
- return (
-
- {/* Back link */}
-
-
- AGENTS
-
+ return (
+
+ {/* Back link */}
+
+
+ AGENTS
+
- {/* Header */}
-
-
-
-
{agent.name}
-
- {status.label}
-
-
-
{agent.agent_id}
-
+ {/* Header */}
+
+
+
+
+ {agent.name}
+
+
+ {status.label}
+
+
+
+ {agent.agent_id}
+
+
- {/* Details grid */}
-
-
-
REGISTERED
-
{new Date(agent.registered_at).toLocaleDateString()}
-
-
-
ACTIVE GRANTS
-
{agent.grants.filter(g => g.status === 'active').length}
-
-
-
PUBLIC KEY
-
{agent.public_key.slice(0, 24)}...
-
-
+ {/* Details grid */}
+
+
+
+ REGISTERED
+
+
+ {new Date(
+ agent.registered_at,
+ ).toLocaleDateString()}
+
+
+
+
+ ACTIVE GRANTS
+
+
+ {
+ agent.grants.filter(
+ (g) =>
+ g.status === "active",
+ ).length
+ }
+
+
+
+
+ PUBLIC KEY
+
+
+ {agent.public_key.slice(0, 24)}...
+
+
+
- {/* Grants */}
-
-
GRANTS ({agent.grants.length})
- {agent.grants.length === 0 ? (
-
- ) : (
-
- {agent.grants.map((grant) => (
- setRevokeGrantId(grant.grant_id)}
- />
- ))}
-
- )}
-
+ {/* Grants */}
+
+
+ GRANTS ({agent.grants.length})
+
+ {agent.grants.length === 0 ? (
+
+ ) : (
+
+ {agent.grants.map((grant) => (
+
+ setRevokeGrantId(
+ grant.grant_id,
+ )
+ }
+ />
+ ))}
+
+ )}
+
- {/* Activity */}
-
-
RECENT ACTIVITY ({events.length})
- {events.length === 0 ? (
-
- ) : (
-
- {events.map((event) => (
-
- ))}
-
- )}
-
+ {/* Activity */}
+
+
+ RECENT ACTIVITY ({events.length})
+
+ {events.length === 0 ? (
+
+
+ NO RECENT ACTIVITY
+
+
+ ) : (
+
+ {events.map((event) => (
+
+ ))}
+
+ )}
+
- {/* Danger zone */}
- {agent.status === 'active' && (
-
-
-
- Revoking this agent will immediately terminate all access and invalidate all active tokens.
-
-
-
- )}
-
- );
- })()}
+ {/* Danger zone */}
+ {agent.status === "active" && (
+
+
+
+ Revoking this agent will immediately
+ terminate all access and invalidate
+ all active tokens.
+
+
+
+ )}
+
+ );
+ })()}
- {/* Revoke agent dialog */}
- {showRevokeConfirm && state.type === 'loaded' && (
-
setShowRevokeConfirm(false)}
- />
- )}
+ {/* Revoke agent dialog */}
+ {showRevokeConfirm && state.type === "loaded" && (
+ setShowRevokeConfirm(false)}
+ />
+ )}
- {/* Revoke grant dialog */}
- {revokeGrantId && (
- handleRevokeGrant(revokeGrantId)}
- onCancel={() => setRevokeGrantId(null)}
- />
- )}
-
-
- );
+ {/* Revoke grant dialog */}
+ {revokeGrantId && (
+
handleRevokeGrant(revokeGrantId)}
+ onCancel={() => setRevokeGrantId(null)}
+ />
+ )}
+
+
+ );
}
function SectionLabel({ children }: { children: React.ReactNode }) {
- return (
-
- );
+ return (
+
+ );
}
-function GrantRow({ grant, onRevoke }: { grant: GrantSummary; onRevoke: () => void }) {
- const grantStatus: Record = {
- active: { color: 'bg-green', text: 'text-green', label: 'ACTIVE' },
- approved: { color: 'bg-green', text: 'text-green', label: 'APPROVED' },
- pending: { color: 'bg-amber', text: 'text-amber', label: 'PENDING' },
- denied: { color: 'bg-red', text: 'text-red', label: 'DENIED' },
- revoked: { color: 'bg-red', text: 'text-red', label: 'REVOKED' },
- expired: { color: 'bg-text-muted', text: 'text-text-muted', label: 'EXPIRED' },
- };
+function GrantRow({
+ grant,
+ onRevoke,
+}: {
+ grant: GrantSummary;
+ onRevoke: () => void;
+}) {
+ const grantStatus: Record<
+ string,
+ { color: string; text: string; label: string }
+ > = {
+ active: { color: "bg-green", text: "text-green", label: "ACTIVE" },
+ approved: { color: "bg-green", text: "text-green", label: "APPROVED" },
+ pending: { color: "bg-amber", text: "text-amber", label: "PENDING" },
+ denied: { color: "bg-red", text: "text-red", label: "DENIED" },
+ revoked: { color: "bg-red", text: "text-red", label: "REVOKED" },
+ expired: {
+ color: "bg-text-muted",
+ text: "text-text-muted",
+ label: "EXPIRED",
+ },
+ };
- const fallback = { color: 'bg-text-muted', text: 'text-text-muted', label: grant.status.toUpperCase() };
- const status = grantStatus[grant.status] ?? fallback;
+ const fallback = {
+ color: "bg-text-muted",
+ text: "text-text-muted",
+ label: grant.status.toUpperCase(),
+ };
+ const status = grantStatus[grant.status] ?? fallback;
- return (
-
-
-
-
-
-
{grant.service_provider_name}
-
{status.label}
-
-
- {grant.capabilities.map((cap, idx) => (
-
{capabilityToHumanReadable(cap)}
- ))}
-
-
- {new Date(grant.created_at).toLocaleDateString()}
-
-
-
- {grant.status === 'pending' && (
-
- APPROVE
-
- )}
- {(grant.status === 'active' || grant.status === 'approved') && (
-
- )}
-
-
-
- );
+ return (
+
+
+
+
+
+
+ {grant.service_provider_name}
+
+
+ {status.label}
+
+
+
+ {grant.capabilities.map((cap, idx) => (
+
+ {capabilityToHumanReadable(cap)}
+
+ ))}
+
+
+ {new Date(grant.created_at).toLocaleDateString()}
+
+
+
+ {grant.status === "pending" && (
+
+ APPROVE
+
+ )}
+ {(grant.status === "active" ||
+ grant.status === "approved") && (
+
+ )}
+
+
+
+ );
}
function ActivityRow({ event }: { event: AuditEvent }) {
- const eventLabels: Record = {
- token_issued: 'Token Issued',
- token_verified: 'Token Verified',
- token_denied: 'Token Denied',
- grant_approved: 'Grant Approved',
- grant_denied: 'Grant Denied',
- agent_registered: 'Agent Registered',
- agent_revoked: 'Agent Revoked',
- };
+ const eventLabels: Record = {
+ token_issued: "Token Issued",
+ token_verified: "Token Verified",
+ token_denied: "Token Denied",
+ grant_approved: "Grant Approved",
+ grant_denied: "Grant Denied",
+ agent_registered: "Agent Registered",
+ agent_revoked: "Agent Revoked",
+ };
- const outcomeConfig: Record = {
- allowed: { color: 'text-green', label: 'OK' },
- denied: { color: 'text-red', label: 'DENIED' },
- rate_limited: { color: 'text-amber', label: 'THROTTLED' },
- };
+ const outcomeConfig: Record = {
+ allowed: { color: "text-green", label: "OK" },
+ denied: { color: "text-red", label: "DENIED" },
+ rate_limited: { color: "text-amber", label: "THROTTLED" },
+ };
- const outcome = outcomeConfig[event.outcome] || { color: 'text-text-muted', label: event.outcome };
+ const outcome = outcomeConfig[event.outcome] || {
+ color: "text-text-muted",
+ label: event.outcome,
+ };
- return (
-
-
- {new Date(event.created_at).toLocaleString()}
-
-
- {eventLabels[event.event_type] || event.event_type}
- {event.capability && (
-
- {capabilityToHumanReadable(event.capability)}
-
- )}
-
-
- {outcome.label}
-
-
- );
+ return (
+
+
+ {new Date(event.created_at).toLocaleString()}
+
+
+
+ {eventLabels[event.event_type] || event.event_type}
+
+ {event.capability && (
+
+ {capabilityToHumanReadable(event.capability)}
+
+ )}
+
+
+ {outcome.label}
+
+
+ );
}
function ConfirmDialog({
- title,
- message,
- confirmLabel,
- onConfirm,
- onCancel,
+ title,
+ message,
+ confirmLabel,
+ onConfirm,
+ onCancel,
}: {
- title: string;
- message: string;
- confirmLabel: string;
- onConfirm: () => void;
- onCancel: () => void;
+ title: string;
+ message: string;
+ confirmLabel: string;
+ onConfirm: () => void;
+ onCancel: () => void;
}) {
- return (
-
-
-
-
{message}
-
-
-
-
-
-
- );
+ return (
+
+
+
+
+ {message}
+
+
+
+
+
+
+
+ );
}
diff --git a/services/approval-ui/src/pages/AgentsPage.tsx b/services/approval-ui/src/pages/AgentsPage.tsx
index 827c470..662bb33 100644
--- a/services/approval-ui/src/pages/AgentsPage.tsx
+++ b/services/approval-ui/src/pages/AgentsPage.tsx
@@ -1,183 +1,235 @@
-import { useState, useEffect } from 'react';
-import { Link, useRouter } from '../Router';
-import { listAgents, checkHealth } from '../api';
-import type { AgentSummary } from '../types';
+import { useState, useEffect } from "react";
+import { Link, useRouter } from "../Router";
+import { listAgents, checkHealth } from "../api";
+import type { AgentSummary } from "../types";
type PageState =
- | { type: 'loading' }
- | { type: 'error'; message: string; isOffline: boolean }
- | { type: 'loaded'; agents: AgentSummary[] };
+ | { type: "loading" }
+ | { type: "error"; message: string; isOffline: boolean }
+ | { type: "loaded"; agents: AgentSummary[] };
export function AgentsPage() {
- const [state, setState] = useState({ type: 'loading' });
+ const [state, setState] = useState({ type: "loading" });
- useEffect(() => {
- loadAgents();
- }, []);
+ useEffect(() => {
+ loadAgents();
+ }, []);
- async function loadAgents() {
- setState({ type: 'loading' });
- const isHealthy = await checkHealth();
- if (!isHealthy) {
- setState({
- type: 'error',
- message: 'Unable to establish connection with AgentAuth registry.',
- isOffline: true,
- });
- return;
- }
- try {
- const agents = await listAgents();
- setState({ type: 'loaded', agents });
- } catch (err) {
- setState({
- type: 'error',
- message: err instanceof Error ? err.message : 'Failed to load agents',
- isOffline: false,
- });
- }
- }
+ async function loadAgents() {
+ setState({ type: "loading" });
+ const isHealthy = await checkHealth();
+ if (!isHealthy) {
+ setState({
+ type: "error",
+ message:
+ "Unable to establish connection with AgentAuth registry.",
+ isOffline: true,
+ });
+ return;
+ }
+ try {
+ const agents = await listAgents();
+ setState({ type: "loaded", agents });
+ } catch (err) {
+ setState({
+ type: "error",
+ message:
+ err instanceof Error
+ ? err.message
+ : "Failed to load agents",
+ isOffline: false,
+ });
+ }
+ }
- return (
-
- {/* Top bar */}
-
-
-
-
-
AGENTAUTH
-
-
AGENTS
-
-
+ return (
+
+ {/* Top bar */}
+
+
+
+
+
+ AGENTAUTH
+
+
+
+ AGENTS
+
+
+
-
- {state.type === 'loading' && (
-
-
-
-
- {[1, 2, 3].map((i) => (
-
- ))}
-
-
- )}
+
+ {state.type === "loading" && (
+
+
+
+
+ {[1, 2, 3].map((i) => (
+
+ ))}
+
+
+ )}
- {state.type === 'error' && (
-
-
-
-
-
-
- {state.isOffline ? 'CONNECTION LOST' : 'ERROR'}
-
-
{state.message}
-
-
-
-
-
- )}
+ {state.type === "error" && (
+
+
+
+
+
+
+ {state.isOffline
+ ? "CONNECTION LOST"
+ : "ERROR"}
+
+
+ {state.message}
+
+
+
+
+
+
+ )}
- {state.type === 'loaded' && (
-
- {/* Header */}
-
-
-
- {state.agents.length} registered agent{state.agents.length !== 1 ? 's' : ''}
-
-
+ {state.type === "loaded" && (
+
+ {/* Header */}
+
+
+
+ {state.agents.length} registered agent
+ {state.agents.length !== 1 ? "s" : ""}
+
+
- {state.agents.length === 0 ? (
-
-
-
NO AGENTS REGISTERED
-
No agents have been authorized to act on your behalf.
-
- ) : (
-
- {state.agents.map((agent) => (
-
- ))}
-
- )}
-
- )}
-
-
- );
+ {state.agents.length === 0 ? (
+
+
+
+ NO AGENTS REGISTERED
+
+
+ No agents have been authorized to act on
+ your behalf.
+
+
+ ) : (
+
+ {state.agents.map((agent) => (
+
+ ))}
+
+ )}
+
+ )}
+
+
+ );
}
function AgentRow({ agent }: { agent: AgentSummary }) {
- const { navigate } = useRouter();
- const statusConfig = {
- active: { color: 'bg-green', text: 'text-green', label: 'ACTIVE' },
- suspended: { color: 'bg-amber', text: 'text-amber', label: 'SUSPENDED' },
- revoked: { color: 'bg-red', text: 'text-red', label: 'REVOKED' },
- };
+ const { navigate } = useRouter();
+ const statusConfig = {
+ active: { color: "bg-green", text: "text-green", label: "ACTIVE" },
+ suspended: {
+ color: "bg-amber",
+ text: "text-amber",
+ label: "SUSPENDED",
+ },
+ revoked: { color: "bg-red", text: "text-red", label: "REVOKED" },
+ };
- const status = statusConfig[agent.status];
+ const status = statusConfig[agent.status];
- return (
-
-
- {/* Status indicator */}
-
+ return (
+
navigate(`/agents/${agent.agent_id}/activity`)}
+ >
+
+ {/* Status indicator */}
+
- {/* Agent info — clickable area navigates to detail */}
-
+ {/* Agent info — clickable area navigates to detail */}
+
- {/* Grants count */}
-
-
{agent.active_grants}
-
GRANTS
-
+ {/* Grants count */}
+
+
+ {agent.active_grants}
+
+
+ GRANTS
+
+
- {/* Approve button (pending) or arrow (normal) */}
- {agent.pending_grant_id ? (
-
- APPROVE
-
- ) : (
-
- )}
-
-
- );
+ {/* Approve button (pending) or arrow (normal) */}
+ {agent.pending_grant_id ? (
+
+ APPROVE
+
+ ) : (
+
+ )}
+
+
+ );
}
diff --git a/services/approval-ui/src/pages/ApprovalPage.tsx b/services/approval-ui/src/pages/ApprovalPage.tsx
index b5f0881..6299592 100644
--- a/services/approval-ui/src/pages/ApprovalPage.tsx
+++ b/services/approval-ui/src/pages/ApprovalPage.tsx
@@ -1,452 +1,576 @@
-import { useState, useEffect } from 'react';
-import { useParams, useRouter, Link } from '../Router';
-import { getGrantRequest, approveGrant, denyGrant, checkHealth } from '../api';
+import { useState, useEffect } from "react";
+import { useParams, useRouter, Link } from "../Router";
+import { getGrantRequest, approveGrant, denyGrant, checkHealth } from "../api";
import {
- capabilityToHumanReadable,
- envelopeToHumanReadable,
- requiresTwoStep,
- getCapabilityRiskLevel,
- getCapabilitySummary,
-} from '../utils/capabilities';
-import type { GrantRequest, Capability } from '../types';
+ capabilityToHumanReadable,
+ envelopeToHumanReadable,
+ requiresTwoStep,
+ getCapabilityRiskLevel,
+ getCapabilitySummary,
+} from "../utils/capabilities";
+import type { GrantRequest, Capability } from "../types";
type PageState =
- | { type: 'loading' }
- | { type: 'error'; message: string; isOffline: boolean }
- | { type: 'loaded'; grant: GrantRequest }
- | { type: 'confirming'; grant: GrantRequest; step: 1 | 2 }
- | { type: 'signing'; grant: GrantRequest }
- | { type: 'success'; action: 'approved' | 'denied' }
- | { type: 'expired' };
+ | { type: "loading" }
+ | { type: "error"; message: string; isOffline: boolean }
+ | { type: "loaded"; grant: GrantRequest }
+ | { type: "confirming"; grant: GrantRequest; step: 1 | 2 }
+ | { type: "signing"; grant: GrantRequest }
+ | { type: "success"; action: "approved" | "denied" }
+ | { type: "expired" };
export function ApprovalPage() {
- const { grant_id } = useParams<{ grant_id: string }>();
- const { navigate } = useRouter();
- const [state, setState] = useState({ type: 'loading' });
- const [denyReason, setDenyReason] = useState('');
+ const { grant_id } = useParams<{ grant_id: string }>();
+ const { navigate } = useRouter();
+ const [state, setState] = useState({ type: "loading" });
+ const [denyReason, setDenyReason] = useState("");
- useEffect(() => {
- loadGrant();
- }, [grant_id]);
+ useEffect(() => {
+ loadGrant();
+ }, [grant_id]);
- async function loadGrant() {
- setState({ type: 'loading' });
- const isHealthy = await checkHealth();
- if (!isHealthy) {
- setState({
- type: 'error',
- message: 'Unable to establish connection with AgentAuth registry. Service may be offline.',
- isOffline: true,
- });
- return;
- }
- try {
- const grant = await getGrantRequest(grant_id);
- if (grant.status === 'expired') {
- setState({ type: 'expired' });
- return;
- }
- if (grant.status !== 'pending') {
- setState({
- type: 'error',
- message: `This grant request has already been ${grant.status}.`,
- isOffline: false,
- });
- return;
- }
- setState({ type: 'loaded', grant });
- } catch (err) {
- setState({
- type: 'error',
- message: err instanceof Error ? err.message : 'Failed to load grant request',
- isOffline: false,
- });
- }
- }
+ async function loadGrant() {
+ setState({ type: "loading" });
+ const isHealthy = await checkHealth();
+ if (!isHealthy) {
+ setState({
+ type: "error",
+ message:
+ "Unable to establish connection with AgentAuth registry. Service may be offline.",
+ isOffline: true,
+ });
+ return;
+ }
+ try {
+ const grant = await getGrantRequest(grant_id);
+ if (grant.status === "expired") {
+ setState({ type: "expired" });
+ return;
+ }
+ if (grant.status !== "pending") {
+ setState({
+ type: "error",
+ message: `This grant request has already been ${grant.status}.`,
+ isOffline: false,
+ });
+ return;
+ }
+ setState({ type: "loaded", grant });
+ } catch (err) {
+ setState({
+ type: "error",
+ message:
+ err instanceof Error
+ ? err.message
+ : "Failed to load grant request",
+ isOffline: false,
+ });
+ }
+ }
- function handleApproveClick(grant: GrantRequest) {
- const summary = getCapabilitySummary(grant.requested_capabilities);
- if (summary.hasHighRisk) {
- setState({ type: 'confirming', grant, step: 1 });
- } else {
- startSigning(grant);
- }
- }
+ function handleApproveClick(grant: GrantRequest) {
+ const summary = getCapabilitySummary(grant.requested_capabilities);
+ if (summary.hasHighRisk) {
+ setState({ type: "confirming", grant, step: 1 });
+ } else {
+ startSigning(grant);
+ }
+ }
- function handleConfirmStep1(grant: GrantRequest) {
- setState({ type: 'confirming', grant, step: 2 });
- }
+ function handleConfirmStep1(grant: GrantRequest) {
+ setState({ type: "confirming", grant, step: 2 });
+ }
- async function startSigning(grant: GrantRequest) {
- setState({ type: 'signing', grant });
- try {
- // Generate a random nonce (32 bytes as hex)
- const nonceBytes = new Uint8Array(32);
- crypto.getRandomValues(nonceBytes);
- const nonce = Array.from(nonceBytes, (b) => b.toString(16).padStart(2, '0')).join('');
+ async function startSigning(grant: GrantRequest) {
+ setState({ type: "signing", grant });
+ try {
+ // Generate a random nonce (32 bytes as hex)
+ const nonceBytes = new Uint8Array(32);
+ crypto.getRandomValues(nonceBytes);
+ const nonce = Array.from(nonceBytes, (b) =>
+ b.toString(16).padStart(2, "0"),
+ ).join("");
- // Generate a dummy signature (demo mode — WebAuthn requires HTTPS)
- const sigBytes = new Uint8Array(64);
- crypto.getRandomValues(sigBytes);
- const signature = Array.from(sigBytes, (b) => b.toString(16).padStart(2, '0')).join('');
+ // Generate a dummy signature (demo mode — WebAuthn requires HTTPS)
+ const sigBytes = new Uint8Array(64);
+ crypto.getRandomValues(sigBytes);
+ const signature = Array.from(sigBytes, (b) =>
+ b.toString(16).padStart(2, "0"),
+ ).join("");
- await approveGrant(grant.grant_id, grant.human_principal_id, nonce, signature);
- setState({ type: 'success', action: 'approved' });
- } catch (err) {
- setState({
- type: 'error',
- message: err instanceof Error ? err.message : 'Approval failed',
- isOffline: false,
- });
- }
- }
+ await approveGrant(
+ grant.grant_id,
+ grant.human_principal_id,
+ nonce,
+ signature,
+ );
+ setState({ type: "success", action: "approved" });
+ } catch (err) {
+ setState({
+ type: "error",
+ message: err instanceof Error ? err.message : "Approval failed",
+ isOffline: false,
+ });
+ }
+ }
- async function handleDeny(grant: GrantRequest) {
- try {
- await denyGrant(grant.grant_id, denyReason || undefined);
- setState({ type: 'success', action: 'denied' });
- } catch (err) {
- setState({
- type: 'error',
- message: err instanceof Error ? err.message : 'Failed to deny request',
- isOffline: false,
- });
- }
- }
+ async function handleDeny(grant: GrantRequest) {
+ try {
+ await denyGrant(grant.grant_id, denyReason || undefined);
+ setState({ type: "success", action: "denied" });
+ } catch (err) {
+ setState({
+ type: "error",
+ message:
+ err instanceof Error
+ ? err.message
+ : "Failed to deny request",
+ isOffline: false,
+ });
+ }
+ }
- // --- Loading ---
- if (state.type === 'loading') {
- return (
-
-
-
- );
- }
+ // --- Loading ---
+ if (state.type === "loading") {
+ return (
+
+
+
+ );
+ }
- // --- Error ---
- if (state.type === 'error') {
- return (
-
-
-
-
-
-
-
- {state.isOffline ? 'CONNECTION LOST' : 'ERROR'}
-
-
{state.message}
-
-
-
-
-
-
- );
- }
+ // --- Error ---
+ if (state.type === "error") {
+ return (
+
+
+
+
+
+
+
+ {state.isOffline
+ ? "CONNECTION LOST"
+ : "ERROR"}
+
+
+ {state.message}
+
+
+
+
+
+
+
+ );
+ }
- // --- Expired ---
- if (state.type === 'expired') {
- return (
-
-
-
-
-
REQUEST EXPIRED
-
This grant request has expired and can no longer be processed.
-
-
-
-
- );
- }
+ // --- Expired ---
+ if (state.type === "expired") {
+ return (
+
+
+
+
+
+ REQUEST EXPIRED
+
+
+ This grant request has expired and can no longer be
+ processed.
+
+
+
+
+
+ );
+ }
- // --- Success ---
- if (state.type === 'success') {
- const approved = state.action === 'approved';
- return (
-
-
-
-
-
- {approved ? 'GRANT AUTHORIZED' : 'REQUEST DENIED'}
-
-
- {approved
- ? 'Agent access has been granted. Token issuance is now active.'
- : 'The request has been denied. The agent will be notified.'}
-
-
-
-
-
- );
- }
+ // --- Success ---
+ if (state.type === "success") {
+ const approved = state.action === "approved";
+ return (
+
+
+
+
+
+ {approved ? "GRANT AUTHORIZED" : "REQUEST DENIED"}
+
+
+ {approved
+ ? "Agent access has been granted. Token issuance is now active."
+ : "The request has been denied. The agent will be notified."}
+
+
+
+
+
+ );
+ }
- // --- Signing ---
- if (state.type === 'signing') {
- return (
-
-
-
-
-
PROCESSING
-
Submitting approval to registry...
-
-
-
- );
- }
+ // --- Signing ---
+ if (state.type === "signing") {
+ return (
+
+
+
+
+
+ PROCESSING
+
+
+ Submitting approval to registry...
+
+
+
+
+ );
+ }
- // --- Loaded / Confirming ---
- const grant = state.grant;
- const isConfirming = state.type === 'confirming';
- const confirmStep = isConfirming ? state.step : 0;
- const summary = getCapabilitySummary(grant.requested_capabilities);
+ // --- Loaded / Confirming ---
+ const grant = state.grant;
+ const isConfirming = state.type === "confirming";
+ const confirmStep = isConfirming ? state.step : 0;
+ const summary = getCapabilitySummary(grant.requested_capabilities);
- return (
-
-
- {/* Header */}
-
+ return (
+
+
+ {/* Header */}
+
+
+
+ {grant.grant_id}
+
+
- {/* Agent + Service grid */}
-
-
-
-
+ {/* Agent + Service grid */}
+
+
+
+
- {/* Capabilities */}
-
-
REQUESTED PERMISSIONS ({grant.requested_capabilities.length})
-
- {grant.requested_capabilities.map((cap, idx) => (
-
- ))}
-
-
+ {/* Capabilities */}
+
+
+ REQUESTED PERMISSIONS (
+ {grant.requested_capabilities.length})
+
+
+ {grant.requested_capabilities.map((cap, idx) => (
+
+ ))}
+
+
- {/* Behavioral constraints */}
-
-
BEHAVIORAL CONSTRAINTS
-
- {envelopeToHumanReadable(grant.requested_envelope).map((desc, idx) => (
-
- {'>'}
- {desc}
-
- ))}
-
-
+ {/* Behavioral constraints */}
+
+
BEHAVIORAL CONSTRAINTS
+
+ {envelopeToHumanReadable(grant.requested_envelope).map(
+ (desc, idx) => (
+
+
+ {">"}
+
+
+ {desc}
+
+
+ ),
+ )}
+
+
- {/* Expiry */}
-
-
-
- EXPIRES {new Date(grant.expires_at).toLocaleString().toUpperCase()}
-
-
+ {/* Expiry */}
+
+
+
+ EXPIRES{" "}
+ {new Date(grant.expires_at)
+ .toLocaleString()
+ .toUpperCase()}
+
+
- {/* Actions */}
- {!isConfirming && (
-
-
- setDenyReason(e.target.value)}
- className="bg-panel border border-border px-3 py-2 text-sm text-text-primary placeholder:text-text-muted font-mono focus:outline-none focus:border-amber w-56"
- />
-
-
-
-
- )}
-
+ {/* Actions */}
+ {!isConfirming && (
+
+
+ setDenyReason(e.target.value)}
+ className="bg-panel border border-border px-3 py-2 text-sm text-text-primary placeholder:text-text-muted font-mono focus:outline-none focus:border-amber w-56"
+ />
+
+
+
+
+ )}
+
- {/* Confirmation overlay */}
- {isConfirming && (
-
-
- {confirmStep === 1 ? (
- <>
-
-
-
HIGH-RISK PERMISSIONS
-
-
- This request includes permissions that could modify or delete your data, or make financial transactions.
-
-
Proceed with caution.
-
-
-
-
- >
- ) : (
- <>
-
-
You are granting access to:
-
- {grant.requested_capabilities
- .filter(requiresTwoStep)
- .map((cap, idx) => (
-
- {capabilityToHumanReadable(cap)}
-
- ))}
-
-
- This cannot be undone without revoking the entire grant.
-
-
-
-
-
- >
- )}
-
-
- )}
-
- );
+ {/* Confirmation overlay */}
+ {isConfirming && (
+
+
+ {confirmStep === 1 ? (
+ <>
+
+
+
+ HIGH-RISK PERMISSIONS
+
+
+
+ This request includes permissions that could
+ modify or delete your data, or make
+ financial transactions.
+
+
+ Proceed with caution.
+
+
+
+
+
+ >
+ ) : (
+ <>
+
+
+
+ FINAL CONFIRMATION
+
+
+
+ You are granting access to:
+
+
+ {grant.requested_capabilities
+ .filter(requiresTwoStep)
+ .map((cap, idx) => (
+
+ {capabilityToHumanReadable(cap)}
+
+ ))}
+
+
+ This cannot be undone without revoking the
+ entire grant.
+
+
+
+
+
+ >
+ )}
+
+
+ )}
+
+ );
}
function Shell({ children }: { children: React.ReactNode }) {
- return (
-
- {/* Top bar */}
-
-
-
-
-
AGENTAUTH
-
-
- AGENTS
-
-
-
- {/* Content */}
-
- {children}
-
-
- );
+ return (
+
+ {/* Top bar */}
+
+
+
+
+
+ AGENTAUTH
+
+
+
+ AGENTS
+
+
+
+ {/* Content */}
+
+ {children}
+
+
+ );
}
-function InfoBlock({ label, value, sub }: { label: string; value: string; sub: string }) {
- return (
-
-
{label}
-
{value}
-
{sub}
-
- );
+function InfoBlock({
+ label,
+ value,
+ sub,
+}: {
+ label: string;
+ value: string;
+ sub: string;
+}) {
+ return (
+
+
+ {label}
+
+
+ {value}
+
+
+ {sub}
+
+
+ );
}
function SectionLabel({ children }: { children: React.ReactNode }) {
- return (
-
- );
+ return (
+
+ );
}
function CapabilityRow({ capability }: { capability: Capability }) {
- const risk = getCapabilityRiskLevel(capability);
- const needsTwoStep = requiresTwoStep(capability);
+ const risk = getCapabilityRiskLevel(capability);
+ const needsTwoStep = requiresTwoStep(capability);
- const riskColors = {
- low: 'bg-green',
- medium: 'bg-amber',
- high: 'bg-red',
- };
+ const riskColors = {
+ low: "bg-green",
+ medium: "bg-amber",
+ high: "bg-red",
+ };
- return (
-
-
-
- {capabilityToHumanReadable(capability)}
-
- {needsTwoStep && (
-
- 2-STEP
-
- )}
-
- );
+ return (
+
+
+
+ {capabilityToHumanReadable(capability)}
+
+ {needsTwoStep && (
+
+ 2-STEP
+
+ )}
+
+ );
}
diff --git a/services/approval-ui/src/pages/DashboardPage.tsx b/services/approval-ui/src/pages/DashboardPage.tsx
index 61b7f23..0218e53 100644
--- a/services/approval-ui/src/pages/DashboardPage.tsx
+++ b/services/approval-ui/src/pages/DashboardPage.tsx
@@ -1,108 +1,142 @@
-import { useState } from 'react';
-import { Link } from '../Router';
+import { useState, useEffect } from "react";
+import { Link } from "../Router";
-type DashboardTab = 'token-verification-slo' | 'circuit-breakers';
+type DashboardTab = "token-verification-slo" | "circuit-breakers";
-const GRAFANA_BASE_URL = 'http://localhost:3000';
+const GRAFANA_BASE_URL = "http://localhost:3000";
const DASHBOARDS: Record = {
- 'token-verification-slo': {
- label: 'TOKEN VERIFICATION SLO',
- uid: 'agentauth-verify-slo',
- },
- 'circuit-breakers': {
- label: 'CIRCUIT BREAKERS',
- uid: 'agentauth-circuit-breakers',
- },
+ "token-verification-slo": {
+ label: "TOKEN VERIFICATION SLO",
+ uid: "agentauth-verify-slo",
+ },
+ "circuit-breakers": {
+ label: "CIRCUIT BREAKERS",
+ uid: "agentauth-circuit-breakers",
+ },
};
function getDashboardUrl(uid: string): string {
- return `${GRAFANA_BASE_URL}/d/${uid}?orgId=1&kiosk`;
+ return `${GRAFANA_BASE_URL}/d/${uid}?orgId=1&kiosk`;
}
export function DashboardPage() {
- const [activeTab, setActiveTab] = useState('token-verification-slo');
- const [iframeError, setIframeError] = useState(false);
+ const [activeTab, setActiveTab] = useState(
+ "token-verification-slo",
+ );
+ const [iframeError, setIframeError] = useState(false);
- const activeDashboard = DASHBOARDS[activeTab];
- const iframeSrc = getDashboardUrl(activeDashboard.uid);
+ // Probe Grafana availability. iframe onError doesn't fire for ERR_CONNECTION_REFUSED.
+ const probeGrafana = () => {
+ const controller = new AbortController();
+ fetch(GRAFANA_BASE_URL, {
+ mode: "no-cors",
+ signal: controller.signal,
+ }).catch(() => setIframeError(true));
+ };
- function handleTabChange(tab: DashboardTab) {
- setActiveTab(tab);
- setIframeError(false);
- }
+ useEffect(() => {
+ probeGrafana();
+ }, []);
- return (
-
- {/* Top bar */}
-
-
-
-
-
AGENTAUTH
-
-
DASHBOARD
-
-
+ const activeDashboard = DASHBOARDS[activeTab];
+ const iframeSrc = getDashboardUrl(activeDashboard.uid);
- {/* Tab bar */}
-
-
- {(Object.entries(DASHBOARDS) as [DashboardTab, { label: string; uid: string }][]).map(
- ([key, dashboard]) => (
-
- ),
- )}
-
-
+ function handleTabChange(tab: DashboardTab) {
+ setActiveTab(tab);
+ setIframeError(false);
+ }
- {/* Dashboard iframe */}
-
- {iframeError ? (
-
-
-
-
-
-
- GRAFANA UNREACHABLE
-
-
- Unable to load the Grafana dashboard. Verify that Grafana is running at{' '}
- {GRAFANA_BASE_URL}.
-
-
-
-
-
-
- ) : (
-
-
- );
+ function handleRetry() {
+ setIframeError(false);
+ probeGrafana();
+ }
+
+ return (
+
+ {/* Top bar */}
+
+
+
+
+
+ AGENTAUTH
+
+
+
+ DASHBOARD
+
+
+
+
+ {/* Tab bar */}
+
+
+ {(
+ Object.entries(DASHBOARDS) as [
+ DashboardTab,
+ { label: string; uid: string },
+ ][]
+ ).map(([key, dashboard]) => (
+
+ ))}
+
+
+
+ {/* Dashboard iframe */}
+
+ {iframeError ? (
+
+
+
+
+
+
+ GRAFANA UNREACHABLE
+
+
+ Unable to load the Grafana dashboard.
+ Verify that Grafana is running at{" "}
+
+ {GRAFANA_BASE_URL}
+
+ .
+
+
+
+
+
+
+ ) : (
+
+
+ );
}
diff --git a/services/approval-ui/src/pages/index.ts b/services/approval-ui/src/pages/index.ts
index b9ce672..e9d3ed2 100644
--- a/services/approval-ui/src/pages/index.ts
+++ b/services/approval-ui/src/pages/index.ts
@@ -1,4 +1,4 @@
-export { ApprovalPage } from './ApprovalPage';
-export { AgentsPage } from './AgentsPage';
-export { AgentActivityPage } from './AgentActivityPage';
-export { DashboardPage } from './DashboardPage';
+export { ApprovalPage } from "./ApprovalPage";
+export { AgentsPage } from "./AgentsPage";
+export { AgentActivityPage } from "./AgentActivityPage";
+export { DashboardPage } from "./DashboardPage";
diff --git a/services/approval-ui/src/server.ts b/services/approval-ui/src/server.ts
index 0ee1c9b..e1c4be8 100644
--- a/services/approval-ui/src/server.ts
+++ b/services/approval-ui/src/server.ts
@@ -3,18 +3,18 @@ import index from "./index.html";
const PORT = process.env.PORT || 3000;
Bun.serve({
- port: PORT,
- routes: {
- "/": index,
- "/approve/:grant_id": index,
- "/agents": index,
- "/agents/:agent_id/activity": index,
- "/dashboard": index,
- },
- development: {
- hmr: true,
- console: true,
- },
+ port: PORT,
+ routes: {
+ "/": index,
+ "/approve/:grant_id": index,
+ "/agents": index,
+ "/agents/:agent_id/activity": index,
+ "/dashboard": index,
+ },
+ development: {
+ hmr: true,
+ console: true,
+ },
});
console.log(`Approval UI running at http://localhost:${PORT}`);
diff --git a/services/approval-ui/src/types.ts b/services/approval-ui/src/types.ts
index 0160e5a..6577b93 100644
--- a/services/approval-ui/src/types.ts
+++ b/services/approval-ui/src/types.ts
@@ -2,101 +2,117 @@
/** Capability types matching the Rust enum (serde rename_all = "snake_case") */
export type Capability =
- | { type: 'read'; resource: string; filter: string | null }
- | { type: 'write'; resource: string; conditions: Record | null }
- | { type: 'transact'; resource: string; max_value: number }
- | { type: 'delete'; resource: string; filter: string | null }
- | { type: 'custom'; namespace: string; name: string; params: Record };
+ | { type: "read"; resource: string; filter: string | null }
+ | {
+ type: "write";
+ resource: string;
+ conditions: Record | null;
+ }
+ | { type: "transact"; resource: string; max_value: number }
+ | { type: "delete"; resource: string; filter: string | null }
+ | {
+ type: "custom";
+ namespace: string;
+ name: string;
+ params: Record;
+ };
/** Behavioral envelope constraints */
export interface BehavioralEnvelope {
- max_requests_per_minute: number;
- max_burst: number;
- requires_human_online: boolean;
- human_confirmation_threshold: number | null;
- allowed_time_windows: TimeWindow[] | null;
- max_session_duration_secs: number;
+ max_requests_per_minute: number;
+ max_burst: number;
+ requires_human_online: boolean;
+ human_confirmation_threshold: number | null;
+ allowed_time_windows: TimeWindow[] | null;
+ max_session_duration_secs: number;
}
/** Time window for allowed operations */
export interface TimeWindow {
- start_hour: number;
- start_minute: number;
- end_hour: number;
- end_minute: number;
- days_of_week: number[];
+ start_hour: number;
+ start_minute: number;
+ end_hour: number;
+ end_minute: number;
+ days_of_week: number[];
}
/** Grant request from the registry */
export interface GrantRequest {
- grant_id: string;
- agent_id: string;
- agent_name: string;
- service_provider_id: string;
- service_provider_name: string;
- human_principal_id: string;
- requested_capabilities: Capability[];
- requested_envelope: BehavioralEnvelope;
- created_at: string;
- expires_at: string;
- status: 'pending' | 'approved' | 'denied' | 'expired';
+ grant_id: string;
+ agent_id: string;
+ agent_name: string;
+ service_provider_id: string;
+ service_provider_name: string;
+ human_principal_id: string;
+ requested_capabilities: Capability[];
+ requested_envelope: BehavioralEnvelope;
+ created_at: string;
+ expires_at: string;
+ status: "pending" | "approved" | "denied" | "expired";
}
/** Agent manifest summary */
export interface AgentSummary {
- agent_id: string;
- name: string;
- registered_at: string;
- status: 'active' | 'suspended' | 'revoked';
- active_grants: number;
- pending_grant_id?: string;
+ agent_id: string;
+ name: string;
+ registered_at: string;
+ status: "active" | "suspended" | "revoked";
+ active_grants: number;
+ pending_grant_id?: string;
}
/** Agent details */
export interface AgentDetails {
- agent_id: string;
- name: string;
- registered_at: string;
- status: 'active' | 'suspended' | 'revoked';
- public_key: string;
- capabilities: Capability[];
- grants: GrantSummary[];
+ agent_id: string;
+ name: string;
+ registered_at: string;
+ status: "active" | "suspended" | "revoked";
+ public_key: string;
+ capabilities: Capability[];
+ grants: GrantSummary[];
}
/** Grant summary for agent details */
export interface GrantSummary {
- grant_id: string;
- service_provider_name: string;
- capabilities: Capability[];
- created_at: string;
- status: string;
+ grant_id: string;
+ service_provider_name: string;
+ capabilities: Capability[];
+ created_at: string;
+ status: string;
}
/** Audit event */
export interface AuditEvent {
- event_id: string;
- agent_id: string;
- event_type: 'token_issued' | 'token_verified' | 'token_denied' | 'grant_approved' | 'grant_denied' | 'agent_registered' | 'agent_revoked';
- service_provider_id: string | null;
- capability: Capability | null;
- outcome: 'allowed' | 'denied' | 'rate_limited';
- created_at: string;
- details?: Record;
+ event_id: string;
+ agent_id: string;
+ event_type:
+ | "token_issued"
+ | "token_verified"
+ | "token_denied"
+ | "grant_approved"
+ | "grant_denied"
+ | "agent_registered"
+ | "agent_revoked";
+ service_provider_id: string | null;
+ capability: Capability | null;
+ outcome: "allowed" | "denied" | "rate_limited";
+ created_at: string;
+ details?: Record;
}
/** Approval assertion to be signed */
export interface ApprovalAssertion {
- grant_id: string;
- agent_id: string;
- granted_capabilities: Capability[];
- behavioral_envelope: BehavioralEnvelope;
- approved_at: string;
- approval_nonce: string;
+ grant_id: string;
+ agent_id: string;
+ granted_capabilities: Capability[];
+ behavioral_envelope: BehavioralEnvelope;
+ approved_at: string;
+ approval_nonce: string;
}
/** API error response */
export interface ApiError {
- error: string;
- code: string;
- details?: Record;
+ error: string;
+ code: string;
+ details?: Record;
}
diff --git a/services/audit-archiver/src/main.rs b/services/audit-archiver/src/main.rs
index 0bccd6c..f1338bf 100644
--- a/services/audit-archiver/src/main.rs
+++ b/services/audit-archiver/src/main.rs
@@ -203,8 +203,7 @@ async fn archive_expired(
}
let key = storage::storage_key(&config.storage.s3_prefix, &partition_info.name);
- archive_single_partition(pool, &*cold_storage, &schema, partition_info, &key, result)
- .await;
+ archive_single_partition(pool, &*cold_storage, &schema, partition_info, &key, result).await;
}
Ok(())
@@ -257,7 +256,9 @@ async fn archive_single_partition(
.iter()
.filter_map(|rows| {
parquet::rows_to_record_batch(rows, schema)
- .map_err(|e| error!(partition = %partition_info.name, error = %e, "record batch error"))
+ .map_err(
+ |e| error!(partition = %partition_info.name, error = %e, "record batch error"),
+ )
.ok()
})
.collect();
@@ -329,8 +330,8 @@ fn init_tracing(log_level: &str) {
/// Waits for a shutdown signal (Ctrl+C or SIGTERM).
#[allow(clippy::expect_used)] // Signal handler setup is infallible in practice;
- // panicking here is appropriate since the process
- // cannot function without signal handling.
+ // panicking here is appropriate since the process
+ // cannot function without signal handling.
async fn shutdown_signal() {
let ctrl_c = async {
tokio::signal::ctrl_c()
diff --git a/services/audit-archiver/src/parquet.rs b/services/audit-archiver/src/parquet.rs
index 0ff7a77..c2a3a52 100644
--- a/services/audit-archiver/src/parquet.rs
+++ b/services/audit-archiver/src/parquet.rs
@@ -2,9 +2,7 @@
use std::sync::Arc;
-use arrow::array::{
- BinaryBuilder, RecordBatch, StringBuilder, TimestampMicrosecondBuilder,
-};
+use arrow::array::{BinaryBuilder, RecordBatch, StringBuilder, TimestampMicrosecondBuilder};
use arrow::datatypes::{DataType, Field, Schema, TimeUnit};
use bytes::Bytes;
use parquet::arrow::ArrowWriter;
@@ -170,7 +168,9 @@ mod tests {
#[test]
fn test_schema_agent_id_is_nullable() {
let schema = audit_events_schema();
- let field = schema.field_with_name("agent_id").expect("test: field exists");
+ let field = schema
+ .field_with_name("agent_id")
+ .expect("test: field exists");
assert!(field.is_nullable());
}
@@ -211,7 +211,10 @@ mod tests {
)
.expect("test: parquet reader");
- let batches: Vec<_> = reader.into_iter().collect::, _>>().expect("test: read batches");
+ let batches: Vec<_> = reader
+ .into_iter()
+ .collect::, _>>()
+ .expect("test: read batches");
assert_eq!(batches.len(), 1);
assert_eq!(batches[0].num_rows(), 1);
}
diff --git a/services/audit-archiver/src/partition.rs b/services/audit-archiver/src/partition.rs
index ee3a807..e6bbd97 100644
--- a/services/audit-archiver/src/partition.rs
+++ b/services/audit-archiver/src/partition.rs
@@ -157,14 +157,10 @@ pub async fn create_partition(pool: &PgPool, year: i32, month: u32) -> Result Result> {
match config.backend {
StorageBackend::S3 => {
- let bucket = config
- .s3_bucket
- .as_deref()
- .ok_or_else(|| ArchiverError::Config("s3_bucket is required when backend=s3".into()))?;
+ let bucket = config.s3_bucket.as_deref().ok_or_else(|| {
+ ArchiverError::Config("s3_bucket is required when backend=s3".into())
+ })?;
let storage = S3Storage::new(config, bucket).await?;
Ok(Box::new(storage))
}
StorageBackend::LocalFs => {
- let path = config
- .local_path
- .as_deref()
- .ok_or_else(|| ArchiverError::Config("local_path is required when backend=local_fs".into()))?;
+ let path = config.local_path.as_deref().ok_or_else(|| {
+ ArchiverError::Config("local_path is required when backend=local_fs".into())
+ })?;
let storage = LocalFsStorage::new(path)?;
Ok(Box::new(storage))
}
@@ -91,9 +89,8 @@ impl S3Storage {
///
/// Returns an error if the AWS SDK configuration fails.
async fn new(config: &StorageConfig, bucket: &str) -> Result {
- let mut aws_config_builder = aws_config::from_env().region(
- aws_config::Region::new(config.s3_region.clone()),
- );
+ let mut aws_config_builder =
+ aws_config::from_env().region(aws_config::Region::new(config.s3_region.clone()));
// Override endpoint for MinIO or other S3-compatible services
if let Some(ref endpoint) = config.s3_endpoint_url {
@@ -157,7 +154,9 @@ impl ColdStorage for S3Storage {
if is_not_found {
Ok(false)
} else {
- Err(ArchiverError::Storage(format!("S3 head_object failed: {err}")))
+ Err(ArchiverError::Storage(format!(
+ "S3 head_object failed: {err}"
+ )))
}
}
}
@@ -181,8 +180,9 @@ impl LocalFsStorage {
/// Returns an error if the directory cannot be created.
fn new(path: &str) -> Result {
let base_path = PathBuf::from(path);
- std::fs::create_dir_all(&base_path)
- .map_err(|e| ArchiverError::Storage(format!("failed to create directory {path}: {e}")))?;
+ std::fs::create_dir_all(&base_path).map_err(|e| {
+ ArchiverError::Storage(format!("failed to create directory {path}: {e}"))
+ })?;
Ok(Self { base_path })
}
}
diff --git a/services/demo-agent/src/main.rs b/services/demo-agent/src/main.rs
index b66fd32..03d40d5 100644
--- a/services/demo-agent/src/main.rs
+++ b/services/demo-agent/src/main.rs
@@ -12,7 +12,6 @@
#![allow(clippy::doc_markdown)]
use anyhow::{bail, Context, Result};
-use ed25519_dalek::Signer;
use axum::extract::Path;
use axum::http::HeaderMap;
use axum::routing::{delete, get, post};
@@ -20,6 +19,7 @@ use axum::Router;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine;
use chrono::{Duration, Utc};
+use ed25519_dalek::Signer;
use ed25519_dalek::SigningKey;
use registry::demo;
use sdk::{
@@ -78,10 +78,7 @@ async fn main() -> Result<()> {
println!();
println!(" ┌─────────────────────────────────────────────────────────┐");
println!(" │ Grant pending! Approve it in the UI: │");
- println!(
- " │ http://localhost:3001/approve/{} │",
- &grant_id[..36]
- );
+ println!(" │ http://localhost:3001/approve/{} │", &grant_id[..36]);
println!(" └─────────────────────────────────────────────────────────┘");
println!();
@@ -92,11 +89,10 @@ async fn main() -> Result<()> {
// Store grant in client (re-request to get approved status)
let sp_id_typed = ServiceProviderId::from_uuid(sp_id);
- match client.request_grant(
- sp_id_typed,
- demo_capabilities(),
- demo_envelope(),
- ).await {
+ match client
+ .request_grant(sp_id_typed, demo_capabilities(), demo_envelope())
+ .await
+ {
Ok(_grant) => info!("Grant loaded into SDK"),
Err(sdk::SdkError::GrantPending { .. }) => {
// Still pending? Shouldn't happen after we waited
@@ -180,8 +176,8 @@ fn create_agent_client() -> Result<(AgentAuthClient, uuid::Uuid, uuid::Uuid)> {
};
let config = SdkConfig::new(REGISTRY_URL).context("Invalid registry URL")?;
- let client =
- AgentAuthClient::new(config, signed, &demo::DEMO_AGENT_KEY_SEED).context("Failed to create SDK client")?;
+ let client = AgentAuthClient::new(config, signed, &demo::DEMO_AGENT_KEY_SEED)
+ .context("Failed to create SDK client")?;
Ok((client, agent_id, sp_id))
}
@@ -290,7 +286,10 @@ async fn issue_token_directly(
bail!("Token issue failed ({status}): {body}");
}
- let token: TokenResp = resp.json().await.context("Failed to parse token response")?;
+ let token: TokenResp = resp
+ .json()
+ .await
+ .context("Failed to parse token response")?;
Ok(token.jti.to_string())
}
@@ -339,7 +338,11 @@ async fn run_demo_requests(token: &str, sp_id: uuid::Uuid) -> Vec {
let reason = body
.get("message")
.and_then(|m| m.as_str())
- .unwrap_or(if status == 200 { "Capability granted" } else { "Unknown" })
+ .unwrap_or(if status == 200 {
+ "Capability granted"
+ } else {
+ "Unknown"
+ })
.to_string();
results.push(DemoResult {
@@ -555,7 +558,10 @@ async fn handle_files_write(headers: HeaderMap) -> axum::response::Response {
}
}
-async fn handle_files_delete(headers: HeaderMap, Path(path): Path) -> axum::response::Response {
+async fn handle_files_delete(
+ headers: HeaderMap,
+ Path(path): Path,
+) -> axum::response::Response {
match verify_token(&headers).await {
Ok(v) if has_capability(v.granted_capabilities.as_ref(), "delete", "files") => {
axum::Json(serde_json::json!({