Skip to content

Aegis Roadmap

Last updated: 2026-06-12

This document tracks planned features, improvements, and technical debt for the Aegis platform. Items are organized by priority and grouped into phases.


Phase 1 — Core UI Completeness ✅

High-priority items to make the existing platform fully functional end-to-end.

Token Management UI

  • Status: ✅ Completed (2026-06-11)
  • Priority: 🔴 Critical
  • Description: API Tokens tab in Settings page for managing agent authentication tokens.
  • Implemented:
  • Token creation dialog (name, project scope, expiry)
  • Show plaintext token once after creation (with copy button + security warning)
  • List tokens with prefix, name, project scope, expiry, status (active/expired/revoked)
  • Revoke button per token
  • tokensApi client (create, list, revoke)

Finding Detail Enhancements

  • Status: ✅ Completed (2026-06-11)
  • Priority: 🔴 Critical
  • Description: Finding detail page displays agent-pushed metadata: CVE, CVSS score (color-coded), seen count, last seen at, verification status.
  • Implemented:
  • CVE display in sidebar details
  • CVSS score with color indicator (red ≥9.0, orange ≥7.0, amber ≥4.0, blue <4.0)
  • seen_count display ("Seen N times") when >1 for dedup visibility
  • last_seen_at timestamp when different from created_at
  • verified status added to findings list filter dropdown
  • Project filter dropdown on findings list page

Org Creation UI

  • Status: ✅ Completed (2026-06-12)
  • Priority: 🔴 Critical
  • Description: Users can create new organizations from the sidebar org switcher. The backend existed but the UI had no dialog wired to it.
  • Implemented:
  • Create Organization dialog (name + auto-generated slug + slug preview)
  • Slug validation (3-50 chars, lowercase alphanumeric + hyphens, reserved slug check)
  • Live subdomain preview (slug.aegis.io)
  • Error display for duplicate slugs, reserved names, and validation failures
  • Backend: improved error handling (409 Conflict for reserved/duplicate slugs, plan allowlist validation)
  • Org switcher "Create Organization" button wired to dialog
  • Auto-switch to new org after creation
  • "No Organization" empty state now clickable to open dialog

Phase 2 — Agent Integration

Port the Aegis scanning agent into the monorepo and connect it to the platform. This is the core value proposition — without the scanning engine, the platform is a dashboard with no data source.

Scanning Agent

  • Status: 🟡 In progress (agent binary complete, UI pending)
  • Priority: 🔴 Critical
  • Description: Port the Aegis scanning agent from local-harness/agents/aegis/ into agent/ in the Aegis monorepo. The agent becomes a standalone Go binary and Docker image that scans codebases using AI-powered personas and pushes findings to the Aegis server via the existing agent ingest API.
  • Architecture:
  • Monorepo layout: agent/ directory with its own go.mod (separate Go module), Dockerfile, and binary
  • External dependency: github.com/pixelvide/localharness/adk (public module) for the agent runtime
  • Binary name: aegis (server binary is already aegis-server, no conflict)
  • Docker image: aegis-agent (separate from aegis-server)
  • Docker Compose: Agent is NOT included in docker-compose.yml. Users run the agent binary or Docker image manually.
  • Agent → Server communication:
  • Agent generates a UUID v7 (time-sortable) as scan_id at the start of each run
  • Agent registers the scan via POST /api/v1/agent/scans before pushing findings
  • Every finding pushed includes this scan_id for correlation
  • Server groups findings by scan_id for display on the Scans page
  • Agent signals completion via PATCH /api/v1/agent/scans/{id} — server computes severity summary from findings
  • Graceful shutdown: agent sends {status: "failed"} on SIGINT/SIGTERM via signal handler
  • Finding reporting via report_finding host tool:
  • Personas call report_finding() directly with structured finding data
  • Reporter pushes to Aegis server via POST /api/v1/agent/findings with 3x exponential backoff retry
  • Findings always saved locally to .aegis/findings.json regardless of server connectivity
  • No file scraping, hooks, or post-scan scanning — clean tool-based integration
  • Configuration (layered resolution):
  • CLI flags > env vars > config.yml (workspace) > config.yml (global)
  • AEGIS_BASE_URL / --report-base-url / reporting.base_url — server URL
  • AEGIS_API_KEY — env-var-only (no CLI flag to avoid process list / shell history leaks)
  • AEGIS_PROJECT_ID / --project / reporting.project_id — project UUID
  • CLI interface:
    # Push to server (API key always from env var)
    AEGIS_API_KEY=aegis_xxx \
      aegis --report-base-url=https://acme.aegis.io --project=<uuid> sharingan
    
    # All via env vars (ideal for CI/Docker)
    AEGIS_BASE_URL=https://acme.aegis.io AEGIS_API_KEY=aegis_xxx AEGIS_PROJECT_ID=<uuid> \
      aegis sharingan
    
    # Offline mode (local findings.json only)
    aegis sharingan
    
    # Docker
    docker run --rm \
      -v /path/to/project:/workspace \
      -e GEMINI_API_KEY=xxx \
      -e AEGIS_API_KEY=aegis_xxx \
      -e AEGIS_BASE_URL=https://acme.aegis.io \
      -e AEGIS_PROJECT_ID=<uuid> \
      aegis-agent sharingan
    
  • Personas ported:
  • 👁️ Sharingan — Full security audit & reconnaissance
  • 🧪 Senku — Supply chain & dependency analysis
  • Killua — Targeted penetration testing
  • Subagents ported:
  • exploit-writer — Creates finding reports + working exploit PoC scripts
  • deep-tracer — Read-only deep analysis of specific files/components
  • Completed (2026-06-12):
  • ✅ Copied agent code from local-harness/agents/aegis/agent/
  • ✅ Created agent/go.mod (separate module with replace directive for local dev)
  • ✅ Added ReportingConfig to config.go (layered resolution with env vars)
  • ✅ Added --report-base-url and --project CLI flags to main.go
  • ✅ Created reporter.go — HostTool-based reporter (report_finding tool)
  • ✅ UUID v7 scan correlation via github.com/google/uuid
  • ✅ Created agent/Dockerfile (golang:1.25-alpine build → ubuntu:24.04 runtime)
  • ✅ Created agent/aegis-run Docker convenience script
  • ✅ Server: scan_id added to AgentFindingRequest (required field + validation)
  • ✅ Documentation: docs/agent.md
  • ✅ Agent scan lifecycle: POST /agent/scans (start) + PATCH /agent/scans/{id} (complete/fail)
  • ✅ Agent signal handler (SIGINT/SIGTERM) sends scan failure on crash
  • ✅ Server computes scan summary from findings on completion
  • ✅ UI: Scans page — card-based layout with severity breakdown bars, stale scan detection
  • Remaining (deferred):
  • UI: Agents page updated with setup/usage instructions (token + CLI command)

Phase 3 — Critical Security Fixes

Exploitable security gaps that must be fixed before onboarding users. A security platform without rate limiting and account lockout is not credible.

API Rate Limiting

  • Status: Not started
  • Priority: 🔴 Critical
  • Description: Rate limit auth endpoints (login, register, password reset, MFA) and agent ingest endpoints to prevent brute-force and abuse. Use Valkey for distributed counters.
  • Requirements:
  • Auth endpoints: 5 attempts per minute per IP for login/register, 3 per hour for password reset
  • Agent endpoints: Per-org token bucket (free: 100 req/min, pro: 1000, enterprise: 10000)
  • Global: 1000 req/min per IP across all endpoints
  • Valkey-backed sliding window counters
  • X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset response headers
  • 429 Too Many Requests with Retry-After header
  • Rate limit middleware in the request chain (before auth, for auth endpoints)

Account Lockout

  • Status: Not started
  • Priority: 🔴 Critical
  • Description: Lock accounts after N consecutive failed login attempts to prevent brute-force password guessing. Currently there is no lockout — unlimited attempts are allowed.
  • Requirements:
  • Track failed login attempts per account in Valkey: login_failures:{user_id} counter with TTL
  • After 5 consecutive failures → lock account for 15 minutes
  • After 10 consecutive failures → lock account for 1 hour
  • After 20 consecutive failures → lock account until manual unlock or password reset
  • Reset counter on successful login
  • Send email notification to user when account is locked
  • common.account_lockouts table: user_id UUID PK, locked_until TIMESTAMPTZ, attempt_count INT, last_attempt_at TIMESTAMPTZ
  • Admin API to manually unlock accounts
  • User-facing error: "Account temporarily locked. Try again in X minutes or reset your password."

MFA Brute-Force Protection

  • Status: Not started
  • Priority: 🔴 Critical
  • Description: OTP codes (6 digits) can currently be guessed endlessly with no attempt limit. An attacker with a valid MFA token can try all 1,000,000 combinations.
  • Requirements:
  • Track MFA verification attempts per MFA token in Valkey: mfa_attempts:{mfa_token} counter with TTL matching MFA token expiry
  • After 3 failed attempts → invalidate the MFA token entirely, force user to re-enter password
  • After 5 failed attempts (across multiple MFA tokens) within 10 minutes → trigger account lockout
  • Rate limit POST /api/v1/auth/mfa/validate to 1 request per 2 seconds per IP
  • Rate limit POST /api/v1/auth/mfa/send-email-otp to 1 request per 30 seconds per MFA token
  • Log all failed MFA attempts to audit log
  • User-facing error: "Too many failed attempts. Please log in again."

require_mfa Enforcement Fix

  • Status: ✅ Completed (2026-06-13)
  • Priority: 🔴 Critical
  • Description: The require_mfa org-level feature flag is now enforced in TenantResolver middleware. When enabled, users without MFA are blocked from all org-scoped endpoints with 403 (E60003).
  • Implemented:
  • require_mfa flag seeded in ProvisionOrgSchema() (provisioned=TRUE, enabled=FALSE)
  • Migration 12 seeds the flag for all existing org schemas
  • TenantResolver middleware checks require_mfa flag + user.MFAEnabled — returns 403 mfa_required_by_org if enforcement is on and user has no MFA
  • Agent (Bearer token) requests are NOT affected — they use TokenAuth middleware
  • UI: OrgProvider detects mfa_required_by_org error and shows full-page interstitial with "Set Up MFA" button redirecting to base domain profile
  • Error code errMFARequiredByOrg (E60003) already existed in middleware and API error constants

Token Refresh Rotation

  • Status: Not started
  • Priority: 🔴 Critical
  • Description: Replace the current single long-lived JWT with a short-lived access token + long-lived refresh token pair. Refresh tokens are rotated on each use (one-time use), preventing replay attacks if a token is stolen.
  • Requirements:
  • Short-lived access token (15 min) + refresh token (7 days) pair
  • common.refresh_tokens table: id UUID PK, user_id UUID, token_hash TEXT, family TEXT (rotation chain), expires_at TIMESTAMPTZ, revoked_at TIMESTAMPTZ
  • POST /api/v1/auth/refresh endpoint: validate refresh token, issue new access+refresh pair, revoke old refresh token
  • Rotation detection: If a revoked refresh token is reused, revoke the entire token family (all descendants) — this indicates theft
  • Update frontend request() helper to auto-refresh on 401
  • Graceful migration: existing sessions continue to work until expiry

Email Verification Enforcement

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: Users can currently use the platform with unverified email addresses. No handler or middleware gates access based on email verification status.
  • Requirements:
  • Add email verification check in TenantResolver or Auth middleware
  • If user's primary email is unverified, return 403 with redirect to verification page
  • Grace period: allow access for 24 hours after registration before enforcing
  • Resend verification email endpoint (already exists)

Error Boundaries (React)

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: No React error boundaries exist — unhandled errors crash the entire app with a white screen.
  • Requirements:
  • Add top-level ErrorBoundary component wrapping the app
  • Add per-page error boundaries for graceful degradation
  • Show user-friendly error message with "Reload" button
  • Log errors to console (and later to server-side error tracking)

CSRF Token Protection

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: Currently relying solely on SameSite=Lax cookies for CSRF protection. While SameSite=Lax prevents most CSRF attacks, it does not protect against top-level navigation attacks (GET-based state changes) and some browser edge cases.
  • Requirements:
  • Generate a CSRF token on login and store in session (Valkey or DB)
  • Return token via a GET /api/v1/auth/csrf endpoint or as a response header
  • Frontend stores token in memory (not localStorage) and sends it as X-CSRF-Token header on all mutating requests (POST/PATCH/DELETE)
  • Server middleware validates X-CSRF-Token matches session token on all mutating requests
  • Exempt agent API (Bearer token auth) from CSRF checks — CSRF only applies to cookie-based auth
  • Token rotation: regenerate on sensitive actions (password change, MFA toggle)
  • Double-submit cookie pattern as fallback for non-SPA clients

Phase 4 — RBAC Enforcement

Roles exist in the data model (owner, admin, member, viewer) and the middleware resolves them into context, but no handler checks the role. A viewer can currently delete findings, revoke tokens, and remove members.

Two-Level Role-Based Access Control (RBAC)

  • Status: Not started
  • Priority: 🔴 Critical
  • Description: Implement a two-tier RBAC system with org-level roles (controlling org-wide access) and project-level roles (controlling per-project access). Inspired by Langfuse's RBAC model, adapted for Aegis's security scanning domain.

Design Overview

Aegis RBAC operates at two scopes:

  1. Org-level role — set in common.org_members.role. Determines default access to all projects in the org and controls org-wide resources (members, feature flags, billing, audit logs).
  2. Project-level role — set in org_<uuid>.project_members. Overrides the org-level default for a specific project. Enables granting access to individual projects without org-wide visibility.

Resolution logic:

effective_access(user, project) =
    project_role(user, project)    ← if explicitly assigned
    ?? org_role(user)              ← fallback to org-level default

  • Org Owner → gets Owner-level access to all projects by default (unless overridden)
  • Org None + Project Member on "Backend API" → can only see that one project's findings/scans
  • Org Member + Project Admin on "Mobile App" → elevated access on that specific project

Org Roles: owner, admin, member, viewer, none

Owner Admin Member Viewer None
Organization CRUD, delete, transfer update
Org Members CUD, read CUD, read read
Projects create, delete create
Org Feature Flags CUD, read CUD, read
Billing / Plan CRUD read
Audit Logs read read
API Tokens (org-wide) CUD, read CUD, read
  • Owner: Full control — org deletion, ownership transfer, billing, all resources.
  • Admin: Manage members, projects, tokens, feature flags. Cannot delete org or transfer ownership.
  • Member: Read-only view of org members. Primary access is through project-level scopes (see below).
  • Viewer: No org-level scopes. Read-only access to projects they can see (all projects, since org role is the default floor).
  • None: No org-wide access at all. User must be explicitly granted project-level roles to access anything. Useful for external contractors, auditors, or per-team scoping.

Project-Level Scopes (inherited from org role as default)

When a user has an org role but no explicit project role, the org role determines their default project-level access across all projects:

Owner Admin Member Viewer None
Project Settings read, update, delete read, update read read
Project Members CUD, read CUD, read read
Findings CUD, read CUD, read CUD, read read
Scans CUD, read, delete CUD, read create, read read
Exploits CUD, read CUD, read read read
API Tokens (project-scoped) CUD, read CUD, read read
Dashboard / Stats read read read read

Key notes: - Member can create scans and triage findings (CUD on findings = change status, add notes) but cannot delete scans or manage project members. - Viewer is strictly read-only across all project resources. - None gets zero access unless a project-level role is explicitly assigned.

Project-Level Roles (TBD)

Project-level roles allow overriding the org-level default for a specific project. The exact project roles and their scopes will be defined in a future iteration.

Expected roles: admin, member, viewer (no owner or none at project level — those are org-level concepts).

Expected storage: org_<uuid>.project_members table:

CREATE TABLE project_members (
    project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
    user_id    UUID NOT NULL REFERENCES common.users(id) ON DELETE CASCADE,
    role       TEXT NOT NULL DEFAULT 'member',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    PRIMARY KEY (project_id, user_id)
);

Implementation Requirements

Data model changes: - Add none as a valid org role in common.org_members.role - Create org_<uuid>.project_members table (new table in org schema) - Update ProvisionOrgSchema() to include project_members table - Add migration for existing org schemas

Backend (Go server): - RBAC middleware or per-handler checks — extract user's org role from common.org_members, check project role from project_members if applicable, compute effective role - Scope-checking helper: hasPermission(ctx, resource, action) bool — used in each handler to gate access - Define permissions as constants: PermFindingsRead, PermFindingsCUD, PermMembersRead, PermMembersCUD, PermProjectsCreate, PermOrgDelete, etc. - Map each role to its set of permissions (org-level and project-level separately) - Return 403 Forbidden with {"error": "insufficient permissions"} when a user lacks the required scope

Frontend (React UI): - Fetch user's effective role via GET /api/v1/auth/me (org role) and project context - Expose role in useAuth() or useOrg() context - Hide/disable UI elements based on role (e.g., hide "Create Project" button for member, hide "Delete" for viewer) - Role dropdown in member management (Settings → Members tab) with tooltip showing what each role can do (like Langfuse's pattern)

API changes: - GET /api/v1/members — include role in response (already done) - PATCH /api/v1/members/{userId} — change a member's org role (new endpoint, admin+ only) - GET /api/v1/projects/{slug}/members — list project members with roles (new) - POST /api/v1/projects/{slug}/members — add project member with role (new) - PATCH /api/v1/projects/{slug}/members/{userId} — change project role (new) - DELETE /api/v1/projects/{slug}/members/{userId} — remove project member (new)


Phase 5 — Org Versioning & Feature Flag Completion

Infrastructure for per-org schema migrations and completing the feature flag system.

Org Versioning

  • Status: Not started
  • Priority: 🔴 Critical
  • Description: Track a schema_version per org so tenants can be on different schema versions. This allows rolling upgrades where org A stays on v1 while org B is upgraded to v2.
  • Design:
  • Add schema_version INTEGER DEFAULT 1 to common.organizations.
  • Each org schema migration records which version it applies. The migration runner checks the org's current version and only applies migrations above that version.
  • Graduated rollout: New migrations can target specific orgs or org plans first (e.g., upgrade enterprise orgs to v2 first, then pro, then free).
  • Rollback safety: Each migration version is immutable once applied. Rollback requires a separate "down" migration at a higher version number.
  • Version-gated features: Org-level feature flags can reference a minimum schema version. If an org hasn't been migrated to the required version, the flag is force-disabled regardless of its setting.
  • Changes needed:
  • Add schema_version column to common.organizations (migration)
  • Add org_schema_migrations table to each org schema to track per-org migration history
  • Update ProvisionOrgSchema() to stamp the latest version
  • Create a migration runner that operates per-org (not globally)
  • Admin API to check org version and trigger upgrades
  • Feature-version coupling: Org feature flags include an optional min_version field — the feature is auto-disabled if the org's schema version is below this threshold

Org Feature Flag Completion

  • Status: Partially implemented
  • Priority: 🔴 Critical
  • Description: The org_feature_flags table, store methods, and API endpoint exist, but only org_wide_tokens is seeded. Need to seed all planned flags, enforce org_wide_tokens in token creation, and build the settings UI.
  • What exists:
  • org_xxx.org_feature_flags table (provisioned in ProvisionOrgSchema)
  • Store interface: IsOrgFeatureActive(), ListOrgFeatureFlags(), SetOrgFeatureEnabled()
  • API: GET/PUT /api/v1/org-features
  • What's missing:
  • Seed additional flags: require_mfa, api_access, scan_docker_mode, advanced_reports, webhooks, sso, custom_domain, ip_restrictions, email_domain_restriction, auto_join
  • Enforce org_wide_tokens flag in token creation handler
  • UI: Settings page tab for org admins to view and toggle org-level features
  • RBAC check on feature flag toggle endpoint (admin+ only, depends on Phase 4)

Phase 6 — Audit Logging & Org Policies

Audit logging is a cross-cutting concern that gets exponentially harder to retrofit. Every handler added without audit hooks is a handler that needs to be instrumented later.

Audit Log

  • Status: Not started
  • Priority: 🔴 Critical
  • Description: Log security-relevant actions (member invite/remove, scan create/delete, finding triage changes, feature flag changes, IP allowlist changes, login/logout, password changes, MFA changes) for compliance. Essential for a security platform.
  • Changes needed:
  • audit_log table in per-org schema: id UUID, actor_id UUID, action TEXT, resource_type TEXT, resource_id UUID, metadata JSONB, ip_address TEXT, user_agent TEXT, created_at TIMESTAMPTZ
  • common.auth_audit_log table for auth events (login, logout, password changes, MFA changes) — not org-scoped
  • Store interface + postgres implementation
  • Write audit entries from handlers (non-blocking via goroutines)
  • UI page to browse and filter audit log (by actor, action, date range)
  • Export to CSV/JSON for compliance reports

API Token Usage Tracking

  • Status: Partially implemented
  • Priority: 🟡 Medium
  • Description: Track and display detailed usage information for API tokens — last used time, source IPs, request counts, and usage history. Currently last_used timestamp is stored and updated on each token auth, but not displayed in the UI and no IP/usage history is recorded.
  • What exists:
  • last_used TIMESTAMPTZ column in org_xxx.api_tokens table
  • UpdateTokenLastUsed() store method (called async on each token auth)
  • last_used field in APIToken model and API response
  • What's missing:
  • Token usage log table: org_xxx.api_token_usageid UUID PK, token_id UUID, ip_address TEXT, user_agent TEXT, endpoint TEXT, status_code INT, created_at TIMESTAMPTZ
  • Last used IP: Add last_used_ip TEXT column to api_tokens table (updated alongside last_used)
  • IP history: Track unique IPs per token (derived from usage log). Display as a list of IPs with first/last seen timestamps.
  • Usage count: Add usage_count BIGINT DEFAULT 0 column to api_tokens (incremented on each use)
  • UI — Token list: Show "Last used" column (relative time, e.g., "2 hours ago") and "Last IP" in the token table
  • UI — Token detail dialog/panel: Click a token row to see:
    • Last used time + IP
    • Total usage count
    • Unique IP addresses (with first/last seen)
    • Recent usage log (last 50 requests: timestamp, IP, endpoint, status)
  • Backend changes:
    • Update TokenAuth middleware to log IP, user agent, endpoint to api_token_usage (async, non-blocking)
    • Update UpdateTokenLastUsed() to also set last_used_ip
    • Increment usage_count on each use
    • New store methods: LogTokenUsage(), GetTokenUsageStats(), ListTokenUsageLog()
    • New API endpoints: GET /api/v1/tokens/{id}/usage (admin+), GET /api/v1/projects/{projectId}/tokens/{id}/usage
  • Retention: Auto-purge usage logs older than 90 days (configurable)

IP Restriction

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: Allow orgs to restrict access to specific IP addresses or CIDR ranges. Controlled via the ip_restrictions org-level feature flag.
  • Requirements:
  • org_xxx.ip_allowlist table: id UUID PK, cidr TEXT NOT NULL, description TEXT, created_by UUID, created_at TIMESTAMPTZ
  • API endpoints: GET/POST/DELETE /api/v1/ip-restrictions (admin+ role)
  • Middleware check: if ip_restrictions flag is enabled, validate request IP against allowlist before granting access
  • Applies to both user (cookie) and agent (bearer token) requests
  • UI: Settings page tab for managing IP allowlist entries
  • Support IPv4 and IPv6 CIDR notation

Email Domain Restriction

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: Allow orgs to restrict which email domains can join. Controlled via the email_domain_restriction org-level feature flag.
  • Requirements:
  • org_xxx.allowed_email_domains table: domain TEXT PK, created_at TIMESTAMPTZ
  • When inviting a user or accepting a join request, validate email domain against the allowlist
  • API endpoints: GET/POST/DELETE /api/v1/email-domains (admin+ role)
  • UI: Settings page tab for managing allowed email domains

Session Management

  • Status: ✅ Completed (2026-06-11)
  • Priority: 🟢 Low
  • Description: Users can view and revoke active sessions from Profile → Active Sessions tab. Sessions store browser, OS, device type (parsed from User-Agent server-side). Valkey cache layer reduces DB load on session revocation checks.
  • Implemented:
  • common.user_sessions table with JTI, IP, user agent, browser, OS, device type
  • Profile page: list sessions, revoke individual, revoke all others
  • Valkey cache for session revocation checks (5-min TTL, graceful fallback to DB)
  • Future:
  • Org admin action: "Force logout all members" (revokes all sessions for users in the org)

Phase 7 — Reports & Analytics

Fill in the sidebar sections that exist as navigation items but have no implementation.

Reports Page

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: /reports route is in the sidebar nav but has no page.
  • Requirements:
  • Generate PDF/HTML security reports for a scan
  • Executive summary with severity charts
  • Detailed findings list with remediation guidance
  • Export to PDF

Analytics Page

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: /analytics route is in the sidebar nav but has no page.
  • Requirements:
  • Trends over time (findings opened vs closed)
  • Severity distribution charts
  • Mean time to remediation
  • Per-scan comparison view
  • Needs new API endpoints for time-series data

Store-Level Pagination

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: The pagination envelope infrastructure is ready (ResultInfo, parseResultInfo, writeList), but no SQL query actually paginates. All list endpoints return ALL rows.
  • Requirements:
  • Add LIMIT/OFFSET and COUNT(*) OVER() to paginated SQL queries
  • Update ListFindings, ListScans, ListProjects, ListAPITokens
  • Use writeList with populated ResultInfo for all list endpoints
  • Frontend: pagination controls on findings, scans, and projects pages

Phase 8 — Project Integration

  • Status: Partially implemented
  • Priority: 🟡 Medium
  • Description: Projects exist as an entity and the scans table already has a project_id column. What's missing is the API support for filtering scans by project and the project detail page.
  • Changes needed:
  • Update scan list API to filter by project
  • Project detail page showing its scans and aggregate findings
  • Agent: pass project context when pushing findings

Project Settings

  • Status: Not started
  • Priority: 🟢 Low
  • Description: Per-project configuration (default persona, auto-scan schedule, notification preferences).

Phase 9 — Integrations

CI/CD Integration Guide

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: Provide ready-to-use GitHub Action, GitLab CI template, and generic CLI examples for pushing findings from CI pipelines.
  • Requirements:
  • Example GitHub Action workflow YAML
  • Example GitLab CI job config
  • curl-based examples for any CI system
  • Document token scoping per project

Webhook Notifications

  • Status: Not started
  • Priority: 🟢 Low
  • Description: Allow orgs to configure webhooks that fire when findings are created, status changes, or agents complete a scan pass. Controlled via the webhooks org-level feature flag.
  • Requirements:
  • org_xxx.webhooks table (URL, events, secret)
  • HMAC-signed POST to webhook URL
  • Retry with exponential backoff

Member Invitation Flow

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: Currently members can only be added if they already have an account. Need an invite-by-email flow where non-registered users receive an email invitation to join an org.
  • Requirements:
  • common.org_invitations table: id UUID PK, org_id UUID, email TEXT, role TEXT, invited_by UUID, token_hash TEXT, expires_at TIMESTAMPTZ, accepted_at TIMESTAMPTZ
  • POST /api/v1/members/invite — send invitation email (admin+ role)
  • POST /api/v1/auth/accept-invite — accept invitation (creates account if needed, adds to org)
  • Invitation email with accept link
  • UI: invite dialog in Settings → Members tab

Split OpenAPI Specs

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: Currently a single OpenAPI spec is served on all domains. Split into two specs served contextually.
  • Requirements:
  • openapi-base.yaml — auth, registration, password reset, MFA, user management endpoints
  • openapi-org.yaml — findings, scans, projects, tokens, members, dashboard, agent ingest endpoints
  • Base domain (aegis.io/api/v1/docs) → serves openapi-base.yaml
  • Org subdomain (acme.aegis.io/api/v1/docs) → serves openapi-org.yaml
  • Swagger UI dynamically loads the correct spec based on domain

Slack / Teams Notifications

  • Status: Not started
  • Priority: 🟢 Low
  • Description: Send scan completion and critical finding alerts to Slack or Microsoft Teams channels.
  • Requirements:
  • org_xxx.notification_channels table (type, webhook_url, events, enabled)
  • Slack incoming webhook integration
  • Teams incoming webhook integration
  • Configurable event filters (severity threshold, scan status changes)

SSO / SAML Integration

  • Status: Not started
  • Priority: 🟢 Low
  • Description: Allow enterprise orgs to configure SAML-based SSO. Controlled via the sso org-level feature flag.
  • Requirements:
  • SAML 2.0 SP implementation
  • org_xxx.sso_config table (entity_id, sso_url, certificate, etc.)
  • Login flow: redirect to IdP → callback → create/link user → set session
  • Support for Okta, Azure AD, Google Workspace, OneLogin
  • Auto-provisioning of users from IdP attributes

Phase 10 — Advanced Security Hardening

Suspicious Activity Detection

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: Detect and alert on potentially malicious login patterns: new device/location, impossible travel, brute-force attempts, and credential stuffing.
  • Requirements:
  • New location/device alert: Compare login IP + UA against user's session history. If new, send an email alert (partially done — login notification emails already sent on every login).
  • Impossible travel detection: If two logins from geographically distant locations within a short time window, flag the newer session and notify the user.
  • Brute-force detection: Track failed login attempts per IP and per account in Valkey. Lock out after N failures (e.g., 5 failures → 15-min lockout).
  • Credential stuffing detection: Track failed login rate per IP across multiple accounts. If an IP fails against many different accounts, block the IP temporarily.
  • GeoIP integration: Add MaxMind GeoLite2 (or ip-api) for IP-to-location resolution. Store country/city in user_sessions.
  • common.security_events table: id UUID, user_id UUID, event_type TEXT, ip TEXT, metadata JSONB, created_at TIMESTAMPTZ
  • Admin dashboard widget showing recent security events
  • Org-level feature flag: suspicious_activity_detection

Password Policy Enforcement

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: Strengthen password validation beyond minimum length. Check passwords against the HaveIBeenPwned breached passwords database (k-anonymity API — only sends a 5-char hash prefix, never the full password).
  • Requirements:
  • Integrate HIBP Pwned Passwords API (k-anonymity model: GET https://api.pwnedpasswords.com/range/{prefix})
  • Reject passwords that appear in known breaches during registration, password change, and password reset
  • Configurable minimum complexity rules (uppercase, digit, special char) — enforce via org-level feature flag strict_password_policy
  • User-facing error: "This password has appeared in a data breach. Please choose a different password."

Session Binding

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: Bind sessions to the originating IP address and User-Agent. If a session cookie is used from a different IP or device, require re-authentication. This prevents stolen cookie replay attacks.
  • Requirements:
  • Store ip_address and user_agent hash in common.user_sessions (already stored — need enforcement)
  • On each authenticated request, compare current IP + UA against the session's stored values
  • Strict mode (org flag strict_session_binding): mismatch → session revoked, user forced to re-login
  • Soft mode (default): mismatch → log a security event + send email alert, but allow the request
  • Handle legitimate IP changes (mobile networks, VPNs) gracefully — soft mode is the safe default

Content Security Policy

  • Status: Not started
  • Priority: 🟡 Medium
  • Description: Add a Content-Security-Policy header to all responses to mitigate XSS, clickjacking, and data injection attacks.
  • Requirements:
  • default-src 'self' — restrict all resources to same-origin by default
  • script-src 'self' — no inline scripts, no eval
  • style-src 'self' 'unsafe-inline' — allow inline styles (needed for shadcn/ui)
  • img-src 'self' data: — allow data URIs for avatars
  • connect-src 'self' — restrict fetch/XHR to same-origin
  • frame-ancestors 'none' — prevent framing (supplements X-Frame-Options)
  • Add CSP header in ServeHTTP alongside existing security headers
  • Consider report-uri / report-to for monitoring violations in production
  • Status: Not started
  • Priority: 🟡 Medium
  • Description: Harden auth cookies with Secure flag and __Secure- prefix once HTTPS is enforced in production. Currently cookies use HttpOnly, SameSite=Lax, and Path=/ but lack Secure: true (which would break local HTTP dev).
  • Requirements:
  • Add Secure: true to all auth cookies (requires HTTPS)
  • Rename cookie from aegis_token to __Secure-aegis_token (browser enforces Secure + no Domain override)
  • Note: __Host- prefix cannot be used because it disallows Domain attribute, which is required for cross-subdomain cookie sharing
  • Consider making Secure flag configurable via env var for dev vs prod
  • Add CSRF token protection for all mutating cookie-authenticated endpoints (see Phase 3 CSRF item)

Password History

  • Status: Not started
  • Priority: 🟢 Low
  • Description: Prevent users from reusing recent passwords when changing or resetting their password. Stores hashed versions of previous passwords.
  • Requirements:
  • common.password_history table: id UUID PK, user_id UUID, password_hash TEXT, created_at TIMESTAMPTZ
  • On password change/reset, check new password against last N (default: 5) stored hashes via bcrypt
  • Configurable depth via org-level feature flag password_history_depth (default: 5)
  • Automatically prune history entries beyond the configured depth
  • User-facing error: "You cannot reuse any of your last 5 passwords."

Auto-Join by Email Domain

  • Status: Not started
  • Priority: 🟢 Low
  • Description: Automatically add users to an org if their email domain matches a configured domain. Controlled via the auto_join org-level feature flag.
  • Requirements:
  • org_xxx.auto_join_domains table: domain TEXT PK, default_role TEXT DEFAULT 'member', created_at TIMESTAMPTZ
  • On user registration or first login, check if any org has an auto-join domain matching the user's email — if so, auto-add as member with the configured default role
  • API endpoints: GET/POST/DELETE /api/v1/auto-join-domains (owner role only)
  • UI: Settings page tab for configuring auto-join domains and default role

Phase 11 — User Experience

Notifications System

  • Status: Not started
  • Priority: 🟢 Low
  • Description: The sidebar user menu has a "Notifications" item but nothing exists. Notify users when scans complete or critical findings are discovered.

User Account Page

  • Status: ✅ Completed (2026-06-11)
  • Priority: 🟢 Low
  • Description: Profile page with 4 tabs: Emails (multi-email management, verification), Password (change password), Authentication (multi-device MFA, recovery codes), Active Sessions (list, revoke, revoke all).

Search / Command Palette

  • Status: Not started
  • Priority: 🟢 Low
  • Description: The sidebar has a "Search" button at the bottom. Implement a global search / command palette (Cmd+K) to quickly navigate to scans, findings, or settings.

Dark Mode Toggle

  • Status: Not started
  • Priority: 🟢 Low
  • Description: The UI uses shadcn/ui which supports dark mode theming. Add a toggle in settings or the user menu.

Phase 12 — Platform & Enterprise Features

Org Plan Limits

  • Status: Not started
  • Priority: 🟢 Low
  • Description: Enforce usage limits based on org plan (free, pro, enterprise).
  • Requirements:
  • Define limits per plan: max members, max projects, max scans/month, max API tokens, max findings retention
  • common.plan_limits table or hardcoded config
  • Enforce in API handlers (return 402/403 when limit exceeded)
  • UI banner showing usage vs. limit

Data Retention Policies

  • Status: Not started
  • Priority: 🟢 Low
  • Description: Allow orgs to configure how long findings and scan data are retained before automatic cleanup.
  • Requirements:
  • data_retention_days setting in org feature flags or org settings table
  • Background job to purge expired data
  • UI configuration in org settings

Org Branding / White Label

  • Status: Not started
  • Priority: 🟢 Low
  • Description: Allow enterprise orgs to customize the UI with their logo, color scheme, and custom domain.
  • Requirements:
  • org_xxx.branding table (logo_url, primary_color, accent_color)
  • Dynamic theme loading based on org context
  • Custom email templates with org branding

Multi-Region / Data Residency

  • Status: Not started
  • Priority: 🟢 Low
  • Description: Allow orgs to choose their data region for compliance (GDPR, SOC2, etc.).
  • Requirements:
  • data_region column on common.organizations
  • Route org queries to region-specific database
  • Region selector during org creation

Technical Debt

Item Description Priority
~~Standardized API response format~~ ~~No consistent response envelope — each endpoint invents its own shape; no pagination on list endpoints~~ ✅ Completed
~~Structured error codes~~ ~~All API errors return {"error": "message"} — no machine-readable codes for programmatic handling~~ ✅ Completed
~~Request trace IDs~~ ~~No request ID generated per request — logs, errors, and API responses can't be correlated for debugging~~ ✅ Completed
Error boundaries No React error boundaries — unhandled errors crash the whole app Phase 3
Loading states Some pages show raw empty states before data loads 🟢 Low
Test suite No unit tests or integration tests exist 🟡 Medium
OpenAPI spec update Swagger spec needs agent ingest + token endpoints added; split base vs org specs Phase 9
Feature flag consistency Migrate all feature-gated behavior to use the two-tier flag system (global + org) Phase 5

Standardized API Response Envelope & Pagination

  • Status: ✅ Completed (2026-06-12)
  • Priority: 🔴 Critical
  • Description: All API endpoints now use a Cloudflare-style response envelope with consistent success/error formatting.
  • Implemented:
  • Standard success envelope: {"success": true, "request_id": "req_...", "result": {...}}
  • Standard error envelope: {"success": false, "request_id": "req_...", "errors": [{"type": "...", "code": "...", "ref": "E...", "message": "..."}]}
  • Helpers: writeResult, writeResultMessage, writeList, writeMessage, writeApiError, writeValidationErrors
  • Pagination infrastructure: ResultInfo struct, parseResultInfo query parser, writeList for paginated responses
  • Frontend: ApiError class, automatic envelope unwrapping in request(), requestList() helper
  • All ~250 handler call sites migrated from legacy writeJSON/writeError to new helpers
  • Legacy writeJSON/writeError functions deprecated (no callers remain)
  • Not yet implemented:
  • Store-level pagination (paginated SQL queries with COUNT(*) OVER()) — deferred to Phase 7
  • go generate codegen script for error registry — deferred

Structured Error Codes

  • Status: ✅ Completed (2026-06-12)
  • Priority: 🔴 Critical
  • Description: All API errors now return structured error codes with type, code, reference ID, and message.
  • Implemented:
  • Master error registry: errors.yaml (single source of truth)
  • Go error constants: server/internal/api/errors.go (30+ pre-defined ApiError constants)
  • TypeScript error constants: ui/src/lib/error-codes.gen.ts (matching frontend constants)
  • Error code categories: auth_error (E1xxxx), token_error (E2xxxx), tenant_error (E3xxxx), resource_error (E4xxxx), validation_error (E5xxxx), permission_error (E6xxxx), rate_limit_error (E7xxxx), server_error (E9xxxx)
  • WithMessage() and WithDetails() builder methods on ApiError
  • Field-level validation errors via writeValidationErrors() with FieldError details
  • Frontend ApiError class with error code, type, ref, and message

Request Trace IDs

  • Status: ✅ Completed (2026-06-12)
  • Priority: 🔴 Critical
  • Description: Every API request now has a unique request ID propagated through logs, error responses, and response headers.
  • Implemented:
  • Request ID middleware (server/internal/middleware/request_id.go) — generates req_<hex> IDs
  • Request ID context utilities (server/internal/requestid/requestid.go)
  • X-Request-ID response header on all responses
  • Request ID in all error response bodies (request_id field)
  • Request ID in all success response bodies (request_id field)
  • Logger integration: request_id automatically injected into all slog output
  • Accepts incoming X-Request-ID from reverse proxies
  • Middleware is outermost in the chain (runs before auth, CORS, etc.)

Completed ✅

Feature Date
Auth system (register, login, logout, JWT sessions) Done
Multi-tenant architecture (per-org PostgreSQL schemas) Done
Organization CRUD + auto-provisioning Done
Schema migration system Done
Feature flags (global) Done
Finding CRUD + triage status Done
Exploit storage + display Done
Dashboard with aggregate stats Done
Project CRUD Done
Member invite/remove Done
Login page Done
Sidebar navigation + org/project switcher Done
Finding detail page with markdown rendering Done
Settings page (General + Members tabs) Done
Agents page with persona cards Done
Docker Compose single-image deployment Done
Health check endpoints (/healthz, /readyz) 2026-06-11
OpenAPI 3.0 spec + Swagger UI (/api/v1/docs) 2026-06-11
OpenTelemetry metrics + Prometheus exporter (/metrics) 2026-06-11
Agent Ingest API (push-based findings with fingerprint dedup) 2026-06-11
API token management (per-org, optional project scope) 2026-06-11
Bearer token auth middleware + subdomain org resolution 2026-06-11
CVE/CVSS scoring on findings 2026-06-11
Finding verification loop (agent confirms fixes) 2026-06-11
Reserved slug blacklist for org names 2026-06-11
Custom domain support in organizations 2026-06-11
SMTP email service (MailDev in dev, configurable in prod) 2026-06-11
Password reset via email (forgot/reset/change password) 2026-06-11
TOTP-based Multi-Factor Authentication (MFA) 2026-06-11
MFA recovery codes (10 one-time bcrypt-hashed codes) 2026-06-11
Pre-commit hook for Go/TypeScript linting 2026-06-11
Settings → Security tab (password change + MFA setup) 2026-06-11
Email verification (send + verify via token) 2026-06-11
Profile page (account security, email verification) 2026-06-11
Multi-device MFA (TOTP + email OTP, device CRUD) 2026-06-11
Multi-email management (add, verify, set primary, remove) 2026-06-11
Session tracking & management (JTI, list, revoke) 2026-06-11
Profile page redesign (3 tabs: Emails, Auth, Sessions) 2026-06-11
MFA login flow with device selector 2026-06-11
Recovery code regeneration (password-protected) 2026-06-11
Password change notification emails 2026-06-11
Login notification emails (browser, OS, IP, device) 2026-06-11
Rich session data (server-side UA parsing: browser, OS, device type) 2026-06-11
Valkey cache layer (session revocation caching, graceful fallback) 2026-06-11
Profile page redesign (4 tabs: Emails, Password, Auth, Sessions) 2026-06-11
Token Management UI (Settings → API Tokens tab) 2026-06-11
Projects page (/projects with create dialog) 2026-06-11
Finding detail enhancements (CVE, CVSS, seen_count, last_seen_at) 2026-06-11
Findings filters (verified status, project filter) 2026-06-11
Base domain auth restriction (auth only on base domain, cookie domain scoping, subdomain redirect) 2026-06-11
Org Creation UI (create org dialog, slug validation, auto-switch) 2026-06-12
Standardized API response envelope (Cloudflare-style success/result/errors format) 2026-06-12
Structured error codes (30+ codes across 8 categories with errors.yaml registry) 2026-06-12
Request trace IDs (req_<hex> on every request, in logs + headers + response bodies) 2026-06-12
Full handler migration (~250 call sites from legacy to new envelope/error format) 2026-06-12
MFA Enforcement (require_mfa org flag — partial: data model + UI done) 2026-06-11
require_mfa enforcement in TenantResolver middleware + MFA interstitial UI 2026-06-13