Skip to content

Latest commit

Β 

History

History
853 lines (741 loc) Β· 48.7 KB

File metadata and controls

853 lines (741 loc) Β· 48.7 KB

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_usage β€” id 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

Link Scans to Projects

  • 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

Cookie Security Hardening

  • 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