Skip to content

Backend Components

The backend is a FastAPI application organized into six explicit layers following hexagonal architecture. This page maps the app/ directory and explains how the layers relate.

Layer overview

app/entrypoints/    ← process entry points: API server, workers, runners, maintenance, LLM proxy
app/api/            ← HTTP layer: thin route handlers that delegate to use cases via RuntimeContainer
app/application/    ← business logic: use-case classes with Command/Result types
app/domain/         ← pure Pydantic models, state machines, value objects (no I/O)
app/ports/          ← Python Protocol interfaces (no implementations — adapters implement these)
app/adapters/       ← implementations: Postgres, S3, SQS, ECS, Cognito, GitHub, Stripe…
app/composition/    ← wires adapters to ports; builds RuntimeContainer per process role

The HTTP layer (app/api/)

Route handlers in app/api/routers/ are intentionally thin. A route handler:

  1. Extracts and validates request data (FastAPI/Pydantic handles most of this)
  2. Resolves the RuntimeContainer via Depends(get_runtime_container)
  3. Constructs a *Command dataclass from the request
  4. Calls runtime.use_cases.<use_case>.execute(command)
  5. Maps the *Result to a response model and returns it

Business logic does not belong in route handlers. Conditional logic, state transitions, and storage writes belong in use-case classes in application/.

app/api/routes.py assembles all router modules and mounts them under /api (with /v1 as a backwards-compatibility alias).

The application layer (app/application/)

Use-case classes are the heart of the system. Each operation is a class with: - A *Command dataclass for inputs - A *Result dataclass for outputs - An execute(command) -> result method that calls ports (never adapters directly)

Use-case modules are organized by domain subdomain:

Subdomain Use cases
application/projects/ CreateWorkspaceProjectUseCase, UpdateWorkspaceProjectUseCase, DeleteWorkspaceProjectUseCase, GetWorkspaceProjectUseCase, ListWorkspaceProjectsUseCase, CreateRepositoryUseCase, DeleteRepositoryUseCase, ListRepositoriesUseCase, ListProjectRepositoriesUseCase, ListRepositoryOverviewsUseCase, GetRepositoryUseCase, SetRepositoryDefaultScopeUseCase, GetRepositoryTreeUseCase, GetRepositorySourceFileUseCase, ListWorkspaceFindingsUseCase, ListWorkspaceScansUseCase, GetProjectFindingsRollupUseCase, GetProjectScanActivityUseCase, ProjectCatalogFacade, EnqueueRepositoryIngestScheduler
application/ingest/ EnqueueRepositoryIngestUseCase, ExecuteRepositoryIngestUseCase, RunQueuedRepositoryIngestUseCase, RunRepositoryIngestUseCase, CancelRepositoryIngestUseCase, RetryRepositoryIngestUseCase, ReconcileRepositoryIngestUseCase
application/scans/ CreateScanUseCase, ExecuteScanUseCase, RunQueuedScanUseCase, ListRepositoryScansUseCase, PauseScanUseCase, ResumeScanUseCase, CancelScanUseCase, RetryScanUseCase, ReconcileScansUseCase, ScanLiveDetailUseCase, ScanActivityFeedUseCase, WorkerComponentViewUseCase, ProjectScanLogEventUseCase, MaterializeScanLogArtifactsUseCase, ReadScanLogArtifactUseCase, ReadScanActivityReadModelUseCase, BackfillScanLogReadModelUseCase, PlanningArtifactUseCase, ScanCapacityPolicy
application/findings/ ListFindingsUseCase, GetFindingDetailUseCase, MutateFindingUseCase, NormalizeAndUpsertFindingsUseCase, FindingRollupUseCase, FindingVerificationUseCase
application/artifacts/ RecordArtifactsUseCase, ListScanArtifactsUseCase, ListDebugArtifactsUseCase, CreateArtifactDownloadUrlUseCase, ReadGeneratedThreatModelUseCase, MaterializeGeneratedThreatModelUseCase, BuildCanonicalArtifactManifestUseCase
application/billing_policy/ ScanEligibilityUseCase, ReserveScanUsageUseCase, SettleScanUsageUseCase, ScanKeyPolicy
application/operations/ OperationsMaintenanceUseCase

The domain layer (app/domain/)

Pure Pydantic models with no I/O dependencies. Includes:

  • RecordsDomainModel subclasses (e.g., WorkspaceProject, ScanRecord, FindingRecord)
  • State machinesdomain/scans/states.py, domain/ingest/states.py, domain/repositories/states.py
  • Value objects — typed IDs, object references, enumerations
  • Domain eventsDomainEvent base and event type definitions

The ports layer (app/ports/)

Python Protocol interfaces that the application layer depends on. Key ports:

Port Description
ProjectStore CRUD for WorkspaceProject records
RepositoryStore CRUD for Repository records
SnapshotStore CRUD for SourceSnapshot records
ScanStore CRUD for ScanRecord records
FindingStore CRUD for FindingRecord records
ArtifactStore CRUD for ArtifactRecord records
EventStore Append-only DomainEvent store
GenericRecordStore Document store for auxiliary records
ScanQueuePort Enqueue and dequeue scan jobs
RepositoryIngestQueuePort Enqueue and dequeue ingest jobs
ScanRunnerLauncherPort Launch ECS scan runner tasks
RepositoryIngestRunnerLauncherPort Launch ECS ingest runner tasks
VegaCoreEnginePort Interface to the vega-core scan engine
ObjectStoragePort Upload/download objects (S3 or local files)
IdentityPort Validate tokens, look up users
GitHubPort GitHub App API integration
PaymentPort Stripe payment operations
BillingPort Balance, ledger, and billing policy

The adapters layer (app/adapters/)

Concrete implementations of ports. Each port has at least two implementations: one for local development and one for production.

Port Local implementation Production implementation
Storage (all entities) JsonRecordStore / InMemoryRecordStore PostgresRecordStore
Events JsonEventStore PostgresEventStore
Objects LocalObjectStorage S3ObjectStorage + ScanScopedS3Credentials (STS)
Scan queue LocalScanQueue SQSScanQueue
Ingest queue LocalRepositoryIngestQueue SQSRepositoryIngestQueue
Scan runner LocalScanRunnerLauncher ECSScanRunnerLauncher
Ingest runner LocalRepositoryIngestRunnerLauncher ECSRepositoryIngestRunnerLauncher
Identity LocalIdentityAdapter CognitoIdentityAdapter
GitHub InMemoryGitHubAdapter GitHubAppAdapter
Billing StaticBillingAdapter Sub2APIBillingAdapter
Scan keys InMemoryScanKeyAdapter Sub2APIScanKeyAdapter
Payments InMemoryPaymentAdapter StripePaymentAdapter
Worker heartbeat InMemoryWorkerHeartbeatAdapter CloudWatchWorkerHeartbeatAdapter
vega-core engine LocalVegaCoreService VegaCoreEngineAdapter
Agent logs LocalAgentLogSink S3AgentLogSink

The composition layer (app/composition/)

The composition layer is responsible for building a fully-wired RuntimeContainer for each process role. Key components:

RuntimeSettings (composition/settings/) — Pydantic settings loaded from environment variables (VEGA_* prefix), with profile defaults (local, test, staging, production) and optional secrets from AWS Secrets Manager.

build_*_runtime() (composition/wiring/) — One builder function per process role: - build_api_runtime() — for vega-api - build_scan_worker_runtime() — for vega-scan-worker - build_scan_runner_runtime() — for vega-scan-runner - build_repo_ingest_worker_runtime() — for vega-repo-ingest-worker - build_repo_ingest_runner_runtime() — for vega-repo-ingest-runner - build_maintenance_runtime() — for vega-maintenance

Each builder reads the settings, instantiates the appropriate adapter implementations, and wires them together into a RuntimeContainer with all use cases.

Full package map

app/
├── main.py                        Compat shim → entrypoints.api.create_app()
├── llm_proxy/main.py              Compat shim → entrypoints.services.llm_proxy
│
├── api/
│   ├── routes.py                  Assembles all routers under /api (alias: /v1)
│   ├── errors.py                  Structured error envelope helpers
│   ├── dependencies/
│   │   ├── container.py           get_runtime_container FastAPI dependency
│   │   ├── current_user.py        Bearer auth → CurrentUser
│   │   └── request_context.py     user_id + role extraction helpers
│   └── routers/
│       ├── health.py              GET /healthz, /readyz
│       ├── auth.py                POST /auth/login, refresh, logout; GET /auth/config, /auth/me
│       ├── users.py               POST /users/invitations
│       ├── api_keys.py            API key CRUD
│       ├── projects.py            WorkspaceProject CRUD + rollup views
│       ├── repositories.py        Repository workflow (large router, many routes)
│       ├── scans.py               Canonical scan routes
│       ├── findings.py            Canonical finding routes
│       ├── artifacts.py           Scan artifact routes
│       ├── billing.py             Billing management (Stripe, promotions, spend limits)
│       ├── github.py              GitHub App integration
│       ├── git_upload.py          Temporary git-push remotes
│       ├── sessions.py            Legacy upload/analyze flow
│       └── operations.py          /ops/* routes
│
├── application/                   Use-case classes (all business logic)
│   ├── projects/
│   ├── ingest/
│   ├── scans/
│   ├── findings/
│   ├── artifacts/
│   ├── billing_policy/
│   └── operations/
│
├── domain/                        Pure Pydantic models (no I/O)
│   ├── base.py                    DomainModel base class (Pydantic v2)
│   ├── ids.py, object_refs.py, serialization.py
│   ├── artifacts/, billing/, events/, findings/, identity/
│   ├── ingest/, operations/, projects/, repositories/, scans/
│
├── ports/                         Protocol interfaces (no implementations)
│
├── adapters/
│   ├── engine/vega_core/          VegaCoreEngineAdapter, LocalVegaCoreService
│   ├── events/{json,postgres}/
│   ├── identity/{local,cognito}/
│   ├── integrations/{github,stripe,sub2api}/
│   ├── logging/agent_logs.py
│   ├── objects/{local,s3}/
│   ├── operations/{local,cloudwatch}/
│   ├── queues/{local,sqs}/
│   ├── runners/{local,ecs}/
│   └── storage/{json,postgres}/
│
├── composition/
│   ├── container/                 RuntimeContainer, use-case wiring
│   ├── settings/                  RuntimeSettings, loader, profiles, secrets
│   └── wiring/                    build_*_runtime() per process role
│
├── entrypoints/
│   ├── api/                       create_app(), lifespan, middleware
│   ├── workers/                   scan + repository_ingest worker loops
│   ├── runners/                   scan + repository_ingest runner main functions
│   ├── maintenance/               reconcile, cleanup tasks
│   └── services/llm_proxy/        LLM proxy FastAPI app
│
└── storage/migrations/            001–028 SQL migration files

Legacy sessions

The api/routers/sessions.py router and /api/sessions/ routes implement an older upload/analyze flow. New features should use the project/repository model. The legacy path is preserved for backward compatibility.