A web application for configuring Illumina DNA sequencing runs. SeqSetup manages sample information, index assignment, and validation, and generates Illumina Sample Sheet v2 files and associated metadata for instruments including NovaSeq X, MiSeq i100, NextSeq 1000/2000, and others.
- Pixi (for local development)
- Docker and Docker Compose (for containerized deployment)
- MongoDB 7+ (provided automatically by Docker Compose, or installed separately for local development)
This is the recommended way to run a fully functional instance.
# Clone the repository
git clone <repository-url>
cd seqsetup
# Start the application and MongoDB
docker compose up --buildThe application will be available at http://localhost:5001.
To run in the background:
docker compose up --build -dTo stop:
docker compose downMongoDB data is persisted in a named Docker volume (mongo_data). To remove the database volume as well:
docker compose down -vFollow the instructions at pixi.sh to install the Pixi package manager.
Install and start MongoDB 7+ on your local machine. On Ubuntu/Debian:
# See https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/
sudo systemctl start mongodBy default, SeqSetup connects to mongodb://localhost:27017 with database name seqsetup. This can be changed via config/mongodb.yaml or environment variables (see Configuration).
pixi installpixi run serveThe application starts at http://localhost:5001.
No default passwords are committed.
Create a bootstrap user in config/users.yaml with a bcrypt hash:
users:
admin:
display_name: "Administrator"
email: "admin@example.com"
password_hash: "$2b$12$..." # bcrypt hash
role: adminUse AuthService.hash_password(...) (or bcrypt) to generate the hash.
pixi run testThe test suite contains 607 unit tests covering models, services, exporters, validators, and route utilities. Tests run without a database connection.
| Variable | Description | Default |
|---|---|---|
MONGODB_URI |
MongoDB connection URI | mongodb://localhost:27017 |
MONGODB_DATABASE |
Database name | seqsetup |
SEQSETUP_SESSION_SECRET |
Session encryption key | Auto-generated in .sesskey |
INSTRUMENTS_CONFIG |
Path to instruments YAML config | config/instruments.yaml |
Environment variables take precedence over configuration files.
All configuration files are in the config/ directory:
mongodb.yaml-- MongoDB connection settings (URI and database name).users.yaml-- File-based fallback users (bcrypt-hashed passwords). Empty by default; add only bootstrap users needed for your environment.instruments.yaml-- Supported sequencing instruments, flowcell types, reagent kits, SBS chemistry definitions, and default cycle configurations.profiles/-- Application and test profile definitions (can be synced from GitHub).indexes/-- Bundled index kit definitions in CSV and YAML formats.
A session secret key is stored in .sesskey at the project root. It is auto-generated on first startup if it does not exist. Keep this file out of version control. For production, set SEQSETUP_SESSION_SECRET instead.
The authentication system checks credentials in this order:
- LDAP/AD (if configured and enabled)
- Local users in MongoDB (managed through the admin interface)
config/users.yaml(file-based fallback)
To disable file-based fallback users, use one or more of the following approaches:
Remove the YAML users. Replace the contents of config/users.yaml with an empty user list:
users: {}This disables all file-based logins while keeping the file in place. MongoDB local users and LDAP authentication continue to work.
Configure LDAP without local fallback. Set up LDAP/AD authentication through the admin interface and set allow_local_fallback to false. This prevents the local authentication path from being reached entirely, meaning neither MongoDB local users nor users.yaml will be consulted.
Restrict the config mount in Docker. By default, docker-compose.yml bind-mounts the entire config/ directory. You can mount only the files you need and omit users.yaml, which causes file-based authentication to fail with no matching users.
Authentication is configured through the admin interface. Supported methods:
- Local -- Users stored in MongoDB or
config/users.yaml - LDAP -- LDAP directory server
- Active Directory -- Microsoft AD with LDAP protocol
- Administrator -- Full access including index kit management, application/test profiles, local users, API tokens, LDAP configuration, and config sync.
- Standard User -- Run setup, sample management, index assignment, validation, and export functions.
The core workflow is wizard-based:
- Create a new run -- Select instrument platform, flowcell type, reagent kit, and configure cycle counts.
- Add samples -- Paste sample data, upload a file, or import from an external LIMS API (iGene).
- Assign indexes -- Drag-and-drop indexes from uploaded index kits onto samples. Supports unique dual, combinatorial, and single-index modes.
- Validate -- Check for index collisions, color balance issues, and dark cycles. Approve validation.
- Mark Ready -- Locks the run and pre-generates all export files.
- Export -- Download Sample Sheet v2, Sample Sheet v1 (MiSeq), JSON metadata, or validation reports (JSON/PDF).
Runs follow a strict state machine: Draft β Ready β Archived
| Status | Editable | API Access | Exports Available |
|---|---|---|---|
| Draft | Yes | No | No |
| Ready | No | Yes | Yes (pre-generated) |
| Archived | No | Yes | Yes (pre-generated) |
- Draft β Ready requires validation to be approved (no errors, all samples have indexes). Triggers pre-generation of all export files.
- Ready β Draft returns the run to editable state (clears pre-generated exports).
- Ready β Archived marks the run as a historical record.
- Archived is a terminal state -- no transitions out.
| Format | Description |
|---|---|
| Sample Sheet v2 | Illumina CSV for NovaSeq X, MiSeq i100, NextSeq 1000/2000. Includes BCLConvert and DRAGEN sections based on application profiles. |
| Sample Sheet v1 | Legacy CSV format for instruments that require it (MiSeq). |
| JSON Metadata | Complete run and sample data including test IDs, indexes, override cycles, and all configuration. |
| Validation Report (JSON) | Machine-readable validation results with error details, distance matrices, and color balance analysis. |
| Validation Report (PDF) | Human-readable validation summary with heatmaps and color balance charts. |
The API provides programmatic access to finalized runs using Bearer token authentication.
Security: Only ready and archived runs are accessible. Draft runs cannot be accessed via API.
| Endpoint | Description |
|---|---|
GET /api/runs |
List runs (status=ready|archived) |
GET /api/runs/{id}/samplesheet-v2 |
Download Sample Sheet v2 |
GET /api/runs/{id}/samplesheet-v1 |
Download Sample Sheet v1 |
GET /api/runs/{id}/json |
Download JSON metadata |
GET /api/runs/{id}/validation-report |
Download validation JSON |
GET /api/runs/{id}/validation-pdf |
Download validation PDF |
API documentation is available at /api/docs (Swagger UI) and /api/openapi.json.
| Layer | Technology |
|---|---|
| Backend | Python 3.14+, FastHTML framework |
| Database | MongoDB 7+ via PyMongo |
| Frontend | Server-rendered HTML with HTMX for dynamic updates |
| Authentication | Session-based (web UI), Bearer token (API). LDAP/AD via ldap3. |
| Environment | Pixi for dependency management |
| PDF Generation | ReportLab + Matplotlib (for heatmap charts) |
| Testing | pytest (607 unit tests, no database required) |
SeqSetup follows a layered architecture with clear separation of concerns:
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser (HTMX) β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β FastHTML App (app.py) β
β βββ Middleware (middleware.py) β auth β
β βββ Startup (startup.py) β init & DI β
β βββ Context (context.py) β shared state β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Routes (routes/) β Components β
β Request handling, β (components/) β
β input validation, β Server-rendered β
β orchestration β HTML via FastHTML β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Services (services/) β
β Business logic: validation, export, auth, β
β LIMS API client, GitHub sync, parsing β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Repositories (repositories/) β
β Thin MongoDB data access layer β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Models (models/) β
β Python dataclasses with to_dict/from_dict β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β MongoDB β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Data flows top-down: routes receive HTTP requests, call services for business logic, and use repositories for persistence. Components render the UI from model data. Services never import from routes; repositories never import from services.
seqsetup/
βββ config/ # Configuration files
β βββ instruments.yaml # Instrument and flowcell definitions
β βββ mongodb.yaml # Database connection settings
β βββ users.yaml # Development user credentials
β βββ indexes/ # Bundled index kit definitions
β βββ profiles/ # Application/test profiles
β βββ application_profiles/
β βββ test_profiles/
βββ src/seqsetup/ # Application source (~18k lines)
β βββ app.py # FastHTML app creation & route registration
β βββ startup.py # Repo initialization, service factories, DI setup
β βββ middleware.py # Auth beforeware (session + Bearer token)
β βββ context.py # AppContext dataclass for dependency injection
β βββ openapi.py # OpenAPI 3.0 spec for the JSON API
β βββ components/ # UI components (server-rendered FastHTML)
β β βββ wizard/ # Run creation wizard (steps, sample table, indexes)
β β βββ admin/ # Admin pages (auth, instruments, sync, API config)
β β βββ validation/ # Validation page (issues, heatmaps, color balance)
β β βββ layout.py # App shell, navigation, page wrapper
β β βββ edit_run.py # Run overview/editing page components
β β βββ sample_table.py # Sample table for run overview
β β βββ index_panel.py # Index kit display and management
β β βββ dashboard.py # Dashboard / run list
β β βββ login.py # Login form
β β βββ profiles.py # Application/test profile management
β β βββ local_users.py # Local user management
β β βββ api_tokens.py # API token management
β β βββ export_panel.py # Export buttons and download panel
β β βββ run_config.py # Run configuration display
β βββ models/ # Data models (Python dataclasses)
β β βββ sequencing_run.py # SequencingRun, RunStatus, RunCycles, InstrumentPlatform
β β βββ sample.py # Sample (with index assignment)
β β βββ index.py # IndexKit, IndexPair, Index, IndexMode
β β βββ analysis.py # Analysis, AnalysisType, DRAGENPipeline
β β βββ validation.py # ValidationResult, ValidationError models
β β βββ auth_config.py # AuthConfig, AuthMethod, LDAPConfig
β β βββ user.py # User, UserRole
β β βββ api_token.py # ApiToken (bcrypt-hashed)
β β βββ local_user.py # LocalUser (bcrypt-hashed)
β β βββ application_profile.py # ApplicationProfile (SampleSheet sections)
β β βββ test_profile.py # TestProfile (maps test types to app profiles)
β β βββ instrument_config.py # InstrumentConfig
β β βββ instrument_definition.py # InstrumentDefinition (synced from GitHub)
β β βββ sample_api_config.py # SampleApiConfig (external LIMS API settings)
β β βββ profile_sync_config.py # ProfileSyncConfig (GitHub sync settings)
β βββ repositories/ # MongoDB data access
β β βββ base.py # BaseRepository[T], SingletonConfigRepository[C]
β β βββ run_repo.py # SequencingRun CRUD
β β βββ index_kit_repo.py # IndexKit CRUD + index lookup
β β βββ test_repo.py # Legacy test CRUD
β β βββ api_token_repo.py # API token CRUD + verification
β β βββ local_user_repo.py # Local user CRUD + authentication
β β βββ application_profile_repo.py
β β βββ test_profile_repo.py
β β βββ auth_config_repo.py # Singleton: auth config
β β βββ instrument_config_repo.py # Singleton: instrument config
β β βββ sample_api_config_repo.py # Singleton: LIMS API config
β β βββ instrument_definition_repo.py
β β βββ profile_sync_config_repo.py
β βββ routes/ # HTTP route handlers
β β βββ utils.py # Shared guards and helpers
β β βββ main.py # Run detail page (catch-all /runs/{id})
β β βββ dashboard.py # Dashboard / run list
β β βββ wizard.py # New run wizard + add-samples wizard
β β βββ runs.py # Run configuration updates (instrument, cycles, status)
β β βββ samples.py # Sample CRUD, bulk operations, LIMS import
β β βββ indexes.py # Index kit upload, management, download
β β βββ validation.py # Validation page, approve/unapprove
β β βββ export.py # SampleSheet, JSON, and report downloads
β β βββ api.py # JSON API for external integrations
β β βββ swagger.py # Swagger UI at /api/docs
β β βββ auth.py # Login/logout
β β βββ admin.py # Admin pages (auth, instruments, sync, API config)
β β βββ api_tokens.py # API token management
β β βββ local_users.py # Local user management
β β βββ profiles.py # Application/test profile management
β βββ services/ # Business logic
β β βββ validation.py # ValidationService orchestrator (read-only)
β β βββ index_collision_validator.py # Index collision detection + distance matrices
β β βββ color_analysis_validator.py # Dark cycle + color balance checks
β β βββ application_profile_validator.py # Profile compatibility checks
β β βββ samplesheet_v2_exporter.py # Sample Sheet v2 CSV generator
β β βββ samplesheet_v1_exporter.py # Sample Sheet v1 CSV generator
β β βββ json_exporter.py # JSON metadata exporter
β β βββ validation_report.py # Validation JSON + PDF report generators
β β βββ cycle_calculator.py # Override cycles computation
β β βββ index_parser.py # Index kit file parser (CSV/TSV)
β β βββ index_validator.py # Index kit validation
β β βββ index_kit_yaml_exporter.py # Index kit YAML export
β β βββ sample_parser.py # Pasted/uploaded sample data parser
β β βββ sample_api.py # External LIMS API client (iGene)
β β βββ auth.py # AuthService (YAML + MongoDB + LDAP)
β β βββ ldap.py # LDAP/AD authentication
β β βββ database.py # MongoDB connection management
β β βββ github_sync.py # Profile/instrument sync from GitHub
β β βββ scheduler.py # Background sync scheduler
β β βββ log_capture.py # In-memory log capture for admin UI
β β βββ version_resolver.py # Semantic version resolution for profiles
β βββ data/
β β βββ instruments.py # Instrument definitions loader (YAML + DB)
β βββ utils/
β β βββ html.py # XSS protection: escape_js_string, escape_html_attr
β βββ static/ # CSS, JS, images
β βββ css/app.css
β βββ js/app.js
β βββ img/
βββ tests/
β βββ unit/ # 607 unit tests (no database needed)
β βββ integration/ # Integration tests
β βββ fixtures/ # Test data files
β βββ conftest.py
βββ tools/
β βββ mock_igene_api.py # Mock iGene LIMS API server (FastAPI)
βββ Dockerfile
βββ docker-compose.yml
βββ pixi.toml # Pixi project manifest (dependencies, tasks)
βββ pixi.lock # Locked dependency versions
All route modules receive a single AppContext dataclass that holds references to every repository and service factory. This replaces scattered getter functions and makes dependencies explicit:
# In startup.py
ctx = get_app_context() # Creates AppContext with all repos
# In app.py
runs.register(app, rt, ctx)
samples.register(app, rt, ctx)
# In a route handler
run = ctx.run_repo.get_by_id(run_id)AppContext is created once at startup and shared across all route modules.
Repositories are thin data access wrappers around MongoDB collections. Two base classes eliminate boilerplate:
BaseRepository[T]-- For collection-backed entities (runs, index kits, profiles, etc.). Provideslist_all(),get_by_id(),save(),delete().SingletonConfigRepository[C]-- For singleton configuration documents (auth config, instrument config, LIMS API config). Stores config in a sharedsettingscollection keyed byCONFIG_ID.
Repositories contain no business logic. They serialize models via to_dict() / from_dict().
All domain objects are Python @dataclass classes with:
to_dict()-- Serialize to a dict for MongoDB storage.from_dict(cls, data)-- Deserialize from a MongoDB document.__post_init__-- Input validation and normalization (clamping, type coercion).
Models are self-contained and do not import from other layers.
The frontend is entirely server-rendered using FastHTML (a Python framework that generates HTML). Dynamic updates use HTMX attributes (hx-post, hx-get, hx-swap, hx-swap-oob) to replace page fragments without full reloads.
Components are Python functions that return FastHTML element trees:
def SampleRow(sample, run_id, ...):
return Tr(
Td(sample.sample_id),
Td(sample.index1_sequence or ""),
...,
)Large component files are split into packages (e.g., components/wizard/, components/admin/, components/validation/) with __init__.py re-exporting all public symbols to preserve import paths.
Validation is read-only and never mutates run state. The ValidationService orchestrates three specialized validators:
IndexCollisionValidator-- Detects index collisions within lanes, computes Hamming distance matrices.ColorAnalysisValidator-- Checks for dark cycles and color balance issues based on instrument chemistry (two-color vs four-color SBS).ApplicationProfileValidator-- Validates that samples have compatible application profile configurations.
Results are returned as a ValidationResult dataclass containing errors, warnings, and distance matrices.
When a run transitions from Draft to Ready:
- All exports are pre-generated and stored in the
SequencingRunmodel (generated_samplesheet_v2,generated_json, etc.). - The API and export routes serve this pre-generated content, not live exports.
- Validation PDF is generated lazily on first download (to keep the "Mark Ready" transition fast).
This ensures the exported content is a frozen snapshot of the run at the time it was finalized.
Profiles define how the Sample Sheet v2 is structured for different assay types:
- TestProfile maps a
test_id(e.g., "WES", "WGS") to one or more ApplicationProfile references. - ApplicationProfile defines a Sample Sheet section: settings key-value pairs, data fields, and field translations.
This allows the Sample Sheet structure to be configured externally (and synced from GitHub) rather than hardcoded.
SeqSetup can import samples from an external LIMS API (iGene). Configuration is stored in SampleApiConfig with:
- Base URL and API key
- Field mappings to translate LIMS field names to SeqSetup field names
- The mock server (
tools/mock_igene_api.py) implements the iGene API spec for testing
Application profiles, test profiles, instrument definitions, and index kits can be synchronized from a GitHub repository. The ProfileSyncScheduler runs in a background thread and periodically checks for updates based on a configurable interval.
Starting points for common tasks:
| Task | Start here |
|---|---|
| Understand how a request is handled | middleware.py β routes/{module}.py β services/ β repositories/ |
| Add a new route | Look at an existing route module (e.g., routes/runs.py), follow the handler pattern |
| Modify the Sample Sheet output | services/samplesheet_v2_exporter.py (v2) or services/samplesheet_v1_exporter.py (v1) |
| Add a validation check | services/validation.py (orchestrator) and the appropriate sub-validator |
| Change the UI for a page | Find the component in components/, then the route that renders it in routes/ |
| Add a new model field | models/{model}.py -- update the dataclass, to_dict(), and from_dict() |
| Add a new admin page | routes/admin.py + components/admin/{page}.py |
| Modify instrument configuration | config/instruments.yaml + data/instruments.py |
| Debug authentication | middleware.py β services/auth.py β services/ldap.py |
| Understand dependency wiring | startup.py (init_repos, get_app_context) β context.py (AppContext) β app.py (route registration) |
Route registration order matters in app.py because FastHTML matches routes in registration order. More specific routes (e.g., /runs/new/*) must be registered before catch-all patterns (e.g., /runs/{run_id}).
pixi install # Install all dependencies
pixi run serve # Start the application (localhost:5001)
pixi run test # Run all tests
pixi run mock-api # Start the mock iGene API server (localhost:8100)
pixi run docs # Build Sphinx documentation
pixi add <pkg> # Add a runtime dependency
pixi add --feature dev <pkg> # Add a development dependencyA FastAPI-based mock server (tools/mock_igene_api.py) implements the iGene LIMS API for testing the LIMS integration without a real LIMS system. It serves test data for worksheets, samples, and gene panels.
pixi run mock-api
# Or directly: uvicorn tools.mock_igene_api:app --port 8100
# API key for testing: test-api-key-12345The mock server implements the OpenAPI spec defined in igene_openapi.json.
PΓ€r Larsson par.g.larsson@regionvasterbotten.se