diff --git a/app/frontend/src/generated/api-types.ts b/app/frontend/src/generated/api-types.ts
index 8289a58..e94224e 100644
--- a/app/frontend/src/generated/api-types.ts
+++ b/app/frontend/src/generated/api-types.ts
@@ -3866,6 +3866,111 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/activity": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * Activity feed
+ * @description Enriched audit events (severity, category, label) scoped to the caller's visible projects; admins see everything.
+ */
+ get: {
+ parameters: {
+ query?: {
+ severity?: "info" | "success" | "warning" | "error";
+ category?: string;
+ resource?: string;
+ project_id?: string;
+ since?: string;
+ page?: number;
+ limit?: number;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Feed entries */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ activity?: components["schemas"]["AuditLog"][];
+ page?: number;
+ limit?: number;
+ has_more?: boolean;
+ };
+ };
+ };
+ };
+ };
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/projects/{id}/activity": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * Per-project activity timeline
+ * @description Audit events correlated to this project's services, deployments, databases and the project record itself.
+ */
+ get: {
+ parameters: {
+ query?: {
+ page?: number;
+ limit?: number;
+ };
+ header?: never;
+ path: {
+ id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Feed entries */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ activity?: components["schemas"]["AuditLog"][];
+ };
+ };
+ };
+ /** @description Project not found or not visible */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ };
+ };
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/banners/active": {
parameters: {
query?: never;
@@ -10954,6 +11059,15 @@ export interface components {
ip_address?: string;
/** @description Source user agent */
user_agent?: string;
+ /**
+ * @description Event severity for feed display
+ * @enum {string}
+ */
+ severity?: "info" | "success" | "warning" | "error";
+ /** @description Coarse category (service, deployment, project, user, security, system, β¦) */
+ category?: string;
+ /** @description Human-readable event label */
+ label?: string;
/**
* Format: date-time
* @description Timestamp when the audit log entry was created
diff --git a/app/frontend/src/lib/api-client.ts b/app/frontend/src/lib/api-client.ts
index a9458a3..37a7458 100644
--- a/app/frontend/src/lib/api-client.ts
+++ b/app/frontend/src/lib/api-client.ts
@@ -1928,6 +1928,44 @@ export async function getOperations(): Promise
{
return requestJson('/operations');
}
+export type ActivityEntry = {
+ id: string;
+ user_email: string;
+ resource: string;
+ resource_id: string;
+ action: string;
+ details?: string;
+ severity: 'info' | 'success' | 'warning' | 'error';
+ category: string;
+ label: string;
+ created_at: string;
+};
+
+export type ActivityPage = {
+ activity: ActivityEntry[];
+ page?: number;
+ has_more?: boolean;
+};
+
+export async function listActivity(filters: {
+ severity?: string; category?: string; resource?: string;
+ project_id?: string; page?: number; limit?: number;
+} = {}): Promise {
+ const params = new URLSearchParams();
+ if (filters.severity) params.set('severity', filters.severity);
+ if (filters.category) params.set('category', filters.category);
+ if (filters.resource) params.set('resource', filters.resource);
+ if (filters.project_id) params.set('project_id', filters.project_id);
+ if (filters.page) params.set('page', String(filters.page));
+ if (filters.limit) params.set('limit', String(filters.limit));
+ const q = params.toString();
+ return requestJson(`/activity${q ? `?${q}` : ''}`);
+}
+
+export async function listProjectActivity(projectId: string, page = 1): Promise {
+ return requestJson(`/projects/${projectId}/activity?page=${page}&limit=50`);
+}
+
export type Banner = {
id: string;
title: string;
diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml
index 27fac19..3d58648 100644
--- a/docs/api/openapi.yaml
+++ b/docs/api/openapi.yaml
@@ -2620,6 +2620,67 @@ paths:
items:
type: object
+ /activity:
+ get:
+ tags:
+ - Operations
+ summary: Activity feed
+ description: >-
+ Enriched audit events (severity, category, label) scoped to the
+ caller's visible projects; admins see everything.
+ security:
+ - bearerAuth: []
+ parameters:
+ - {name: severity, in: query, schema: {type: string, enum: [info, success, warning, error]}}
+ - {name: category, in: query, schema: {type: string}}
+ - {name: resource, in: query, schema: {type: string}}
+ - {name: project_id, in: query, schema: {type: string, format: uuid}}
+ - {name: since, in: query, schema: {type: string, format: date-time}}
+ - {name: page, in: query, schema: {type: integer, default: 1}}
+ - {name: limit, in: query, schema: {type: integer, default: 50, maximum: 200}}
+ responses:
+ '200':
+ description: Feed entries
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ activity:
+ type: array
+ items:
+ $ref: '#/components/schemas/AuditLog'
+ page: {type: integer}
+ limit: {type: integer}
+ has_more: {type: boolean}
+
+ /projects/{id}/activity:
+ get:
+ tags:
+ - Operations
+ summary: Per-project activity timeline
+ description: Audit events correlated to this project's services, deployments, databases and the project record itself.
+ security:
+ - bearerAuth: []
+ parameters:
+ - {name: id, in: path, required: true, schema: {type: string}}
+ - {name: page, in: query, schema: {type: integer, default: 1}}
+ - {name: limit, in: query, schema: {type: integer, default: 50, maximum: 200}}
+ responses:
+ '200':
+ description: Feed entries
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ activity:
+ type: array
+ items:
+ $ref: '#/components/schemas/AuditLog'
+ '404':
+ description: Project not found or not visible
+
/banners/active:
get:
tags:
@@ -7584,6 +7645,16 @@ components:
user_agent:
type: string
description: Source user agent
+ severity:
+ type: string
+ enum: [info, success, warning, error]
+ description: Event severity for feed display
+ category:
+ type: string
+ description: Coarse category (service, deployment, project, user, security, system, β¦)
+ label:
+ type: string
+ description: Human-readable event label
created_at:
type: string
format: date-time
diff --git a/docs/guides/CLI.md b/docs/guides/CLI.md
index 6d8557a..717ac9b 100644
--- a/docs/guides/CLI.md
+++ b/docs/guides/CLI.md
@@ -84,6 +84,8 @@ webhooks list|create|update|delete|deliveries|test
deliveries are HMAC-signed (X-Containr-Signature)
operations live ops view: active deploys, queue depth,
24h failures, cron runs, backups
+activity enriched activity feed; --severity/--category/
+ --resource/--project/--limit filters
banners list|create|update|delete announcement banners (admin);
active banners print on `auth status`
invites list|create|revoke team invite links (admin); create