* feat: add audit log for feature usage tracking
Add full auditability for all mutating and core API operations across
HTTP, MCP, and system (worker) transports. Audit entries record raw
request/response as JSONB, timing (started_at/ended_at), action, and
transport type.
Backend:
- New audit_log table with JSONB columns for expandability without
future migrations (merge migration of 3 existing heads)
- AuditLogger with fire-and-forget writes via asyncio.create_task
- @audited decorator on 28 HTTP route handlers
- MCP tool audit wrapping for 16 auditable tools
- Worker task execution wrapped with audit_context
- List endpoint with action, transport, date range filters + pagination
- Stats endpoint with per-day counts for charting
- Configurable retention sweep (concurrent-safe DELETE)
Config (env-only, static):
- HINDSIGHT_API_AUDIT_LOG_ENABLED (default: false)
- HINDSIGHT_API_AUDIT_LOG_ACTIONS (comma-separated allowlist, empty=all)
- HINDSIGHT_API_AUDIT_LOG_RETENTION_DAYS (default: -1, keep forever)
Control Plane:
- New "Audit Logs" tab on bank configuration page
- Line chart showing request volume (today/7d/30d) with action filter
- Filterable table with action, transport, date range filters
- Paginated list with detail dialog showing raw request/response JSON
Tests:
- 13 tests covering list, filters, pagination, stats, disabled mode,
action allowlist, and ordering
* fix: split 3-way merge migration into two 2-way merges
Alembic doesn't support 3-parent merge migrations. Split into a no-op
merge of 2 heads (b1c2d3e4f5g6) followed by the audit_log table
migration merging the third head.
* fix: correct merge migration to merge actual 2 heads
The original analysis incorrectly identified 3 heads. There were only 2
(a3b4c5d6e7f8 and c8e5f2a3b4d1). Remove the unnecessary intermediate
merge migration and fix the audit_log migration to merge these 2 heads.
* fix: use 'heads' instead of 'head' in migration runner
Alembic's upgrade('head') fails when multiple heads exist (e.g. from
namespace package overlaps between hindsight-api and hindsight-api-slim).
Using 'heads' (plural) handles this gracefully by upgrading all branches.
* chore: regenerate OpenAPI spec with audit log endpoints
* chore: regenerate TypeScript client and docs skill OpenAPI spec
Python and Go clients still need regeneration (requires Docker).
* chore: regenerate all client SDKs (Python, Go, TypeScript)
Adds generated audit log API clients for Python (audit_api.py),
Go (api_audit.go), and TypeScript client type updates.
85 lines
3 KiB
JSON
85 lines
3 KiB
JSON
{
|
|
"name": "@vectorize-io/hindsight-control-plane",
|
|
"version": "0.4.20",
|
|
"description": "Control plane for Hindsight - Semantic memory system",
|
|
"bin": {
|
|
"hindsight-control-plane": "./bin/cli.js"
|
|
},
|
|
"files": [
|
|
"bin",
|
|
"standalone",
|
|
"public"
|
|
],
|
|
"scripts": {
|
|
"dev": "next dev --turbopack -p ${PORT:-9999}",
|
|
"build": "next build && npm run build:standalone",
|
|
"build:standalone": "rm -rf standalone && SERVER_JS=$(find .next/standalone -path '*/node_modules' -prune -o -name 'server.js' -print | head -1) && test -n \"$SERVER_JS\" || (echo 'Error: server.js not found in .next/standalone - standalone build failed' && exit 1) && STANDALONE_ROOT=$(dirname \"$SERVER_JS\") && cp -r \"$STANDALONE_ROOT\" standalone && cp -r .next/standalone/node_modules standalone/node_modules && mkdir -p standalone/.next && cp -r .next/static standalone/.next/static && mkdir -p standalone/public && (cp -r public/* standalone/public/ 2>/dev/null || true)",
|
|
"start": "next start",
|
|
"lint": "next lint",
|
|
"prepublishOnly": "npm run build"
|
|
},
|
|
"keywords": [
|
|
"hindsight",
|
|
"memory",
|
|
"semantic",
|
|
"ai"
|
|
],
|
|
"author": "Hindsight Team",
|
|
"license": "ISC",
|
|
"dependencies": {
|
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
"@radix-ui/react-checkbox": "^1.3.3",
|
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
"@radix-ui/react-label": "^2.1.8",
|
|
"@radix-ui/react-popover": "^1.1.15",
|
|
"@radix-ui/react-radio-group": "^1.3.8",
|
|
"@radix-ui/react-select": "^2.2.6",
|
|
"@radix-ui/react-slider": "^1.3.6",
|
|
"@radix-ui/react-slot": "^1.2.4",
|
|
"@radix-ui/react-switch": "^1.2.6",
|
|
"@radix-ui/react-tabs": "^1.1.13",
|
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
"@tailwindcss/postcss": "^4.1.17",
|
|
"@tailwindcss/typography": "^0.5.19",
|
|
"@types/cytoscape": "^3.21.9",
|
|
"@types/node": "^24.10.0",
|
|
"@types/react": "^19.2.2",
|
|
"@types/react-dom": "^19.2.2",
|
|
"autoprefixer": "^10.4.21",
|
|
"class-variance-authority": "^0.7.1",
|
|
"clsx": "^2.1.1",
|
|
"cmdk": "^1.1.1",
|
|
"cytoscape": "^3.33.1",
|
|
"cytoscape-fcose": "^2.2.0",
|
|
"eslint": "^9.39.1",
|
|
"eslint-config-next": "^16.0.1",
|
|
"lucide-react": "^0.553.0",
|
|
"next": "^16.1.7",
|
|
"next-themes": "^0.4.6",
|
|
"postcss": "^8.5.6",
|
|
"react": "^19.2.0",
|
|
"react-chrono": "^2.9.1",
|
|
"react-dom": "^19.2.0",
|
|
"react-is": "^19.2.4",
|
|
"react-markdown": "^10.1.0",
|
|
"react18-json-view": "^0.2.9",
|
|
"recharts": "^3.5.1",
|
|
"remark-gfm": "^4.0.1",
|
|
"sonner": "^2.0.7",
|
|
"tailwind-merge": "^3.4.0",
|
|
"tailwindcss": "^4.1.17",
|
|
"tailwindcss-animate": "^1.0.7",
|
|
"three": "^0.182.0",
|
|
"typescript": "^5.9.3"
|
|
},
|
|
"devDependencies": {
|
|
"@eslint/eslintrc": "^3.3.3",
|
|
"@eslint/js": "^9.39.2",
|
|
"@vectorize-io/hindsight-client": "file:../hindsight-clients/typescript",
|
|
"eslint-plugin-react": "^7.37.5",
|
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
"prettier": "^3.7.4",
|
|
"typescript-eslint": "^8.50.0"
|
|
}
|
|
}
|