Projects and Repositories
Projects and repositories are the foundation of the Vega domain model. Almost every other concept — scans, findings, events — exists in the context of a repository inside a project.
Concepts
A WorkspaceProject is a workspace container. Users create projects to group related security work. A project contains one or more repositories.
A Repository is a source code target. It can be: - A Git repository specified by URL — the ingest pipeline clones it - A zip/tar archive uploaded by the user — the ingest pipeline extracts it - A GitHub repository connected via the GitHub App — the ingest pipeline fetches it
After a repository is added, it goes through an ingest pipeline (tracked by an IngestJob). Only repositories in ready state can be scanned.
See the Data Model for the full entity hierarchy.
The ingest pipeline
User adds repository
↓
CreateRepositoryUseCase
├── git URL → set source_kind=git
├── zip upload → set source_kind=zip
└── GitHub → set source_kind=github
↓
EnqueueRepositoryIngestUseCase
→ write IngestJob(state=queued)
→ enqueue to SQS ingest queue (or local queue)
↓
RunQueuedRepositoryIngestUseCase (ingest worker)
→ claim IngestJob (state=running)
↓
├── local mode → ExecuteRepositoryIngestUseCase in-process
└── ecs mode → ECSRepositoryIngestRunnerLauncher.launch()
→ vega-repo-ingest-runner ECS task
↓
ExecuteRepositoryIngestUseCase
├── git clone / zip extract / GitHub API fetch
├── create SourceSnapshot
├── store snapshot (local dir or S3)
└── transition Repository state: creating → snapshotted → ready
Archive safety — when users upload zip archives, the backend enforces limits to prevent zip bomb attacks and path traversal exploits. See Source Ingest for details.
Use cases (projects subdomain)
The application/projects/ module exposes the following use cases:
| Use case | What it does |
|---|---|
CreateWorkspaceProjectUseCase |
Create a new project with name and description |
UpdateWorkspaceProjectUseCase |
Update project name or description |
DeleteWorkspaceProjectUseCase |
Delete project (fails with 409 if repositories remain) |
CreateRepositoryUseCase |
Add a repository (git URL, zip, or GitHub) and enqueue ingest |
ListRepositoriesUseCase |
List repositories for a user or project |
GetRepositoryUseCase |
Get repository details including ingest state |
SetRepositoryDefaultScopeUseCase |
Set the default include-path scope for new scans |
GetRepositoryTreeUseCase |
List files in the repository snapshot |
GetRepositorySourceFileUseCase |
Read a specific file from the snapshot |
ProjectCatalogFacade |
Aggregates project-level rollup views (scan activity, finding counts) |
Route structure
| Router | What it handles |
|---|---|
api/routers/projects.py |
WorkspaceProject CRUD; project-level scan/finding/rollup views |
api/routers/repositories.py |
Repository CRUD; ingest control; source tree; scans; findings (large router) |
Example workflow via API
# Create a project
POST /api/projects
{"name": "My App", "description": "Payment processing service"}
# Add a repository from a Git URL
POST /api/projects/:project_id/repositories
{"name": "backend", "source_url": "https://github.com/org/repo.git"}
# Check ingest status (poll until state = ready)
GET /api/repositories/:repo_id
# Set a scan scope
PUT /api/repositories/:repo_id/default-scope
{"include_paths": ["src/", "lib/"]}
# Start a scan
POST /api/repositories/:repo_id/scans
# Poll scan status
GET /api/scans/:scan_id/live
# Read findings
GET /api/findings/scans/:scan_id
Ingest control
Users can retry or cancel failed ingest jobs:
| Action | Route | Use case |
|---|---|---|
| Cancel | POST /api/repositories/:id/cancel-ingest |
CancelRepositoryIngestUseCase |
| Retry | POST /api/repositories/:id/retry-ingest |
RetryRepositoryIngestUseCase |
Stale ingest jobs are automatically recovered by ReconcileRepositoryIngestUseCase (called by the ingest worker on each loop).
Default scope and special threat modeling
Default scope (default_scope) — a repository can have a default set of include-paths that are pre-filled when creating new scans. Set via PUT /api/repositories/:id/default-scope.
Special threat modeling — a repository can have a custom threat modeling configuration stored in special_threat_modeling_id. Accessible via GET /PUT /api/repositories/:id/special-threat-modeling.
GitHub integration
Repositories connected via the GitHub App have additional metadata fields:
- github_installation_id — the GitHub App installation
- github_repo_full_name — owner/repo
- github_default_branch — used when no branch is specified
GitHub-connected repositories can list branches and pull requests for targeted scanning:
- GET /api/github/connections/:id/repositories — list available repos
- GET /api/github/connections/:id/branches — list branches for a repo
- GET /api/github/connections/:id/pull-requests — list open PRs
Debugging
Repository stuck in creating or snapshotted
- Check API logs for ingest errors from
EnqueueRepositoryIngestUseCase. - If the ingest queue mode is
sqs, confirm the ingest worker is running and consuming the queue. - Check the
IngestJobrecord — querypayload->>'state'from theingest_jobstable. - Check the ingest runner logs in CloudWatch for git clone or archive extraction errors.
Scan not progressing after creation
- Check
VEGA_SCAN_EXECUTION_MODE. Ifexternalorsqs, a scan worker must be running. - Check the
ScanRecordstate — ifqueued, the worker hasn't claimed it yet. - If
runningfor longer than expected, the runner may have crashed silently — check CloudWatch logs. - See Scan Lifecycle for the complete flow.