Architecture
How Snapline Hub is structured: components, data flow, storage, and relationship to the Snapline framework.
System overview
┌─────────────────────────────────────────────────────────────┐
│ Snapline Ecosystem │
├─────────────────────────┬───────────────────────────────────┤
│ snapline (Node.js) │ snapline-python │
│ @vaagatech/snapline-* │ snapline_* packages │
│ │ │
│ testSuite() │ test_suite() │
│ runApiFixtureCases() │ run_api_fixture_cases() │
│ buildReport() │ build_report() │
│ pushTestReportToHub() │ push_test_report_to_hub() │
└────────────┬────────────┴──────────────┬────────────────────┘
│ │
│ POST /api/reports │
│ (TestRunReport JSON) │
▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ snapline-hub │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Express API │ │ SQLite DB │ │ React Dashboard │ │
│ │ server/ │ │ data/*.db │ │ web/src/ │ │
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Design principles
- Optional — not bundled into Snapline npm/PyPI packages
- Single application — one repo, one deployable unit (not split by language)
- Schema-compatible — accepts the same
TestRunReportfrom Node.js and Python - Pluggable storage — SQLite by default; PostgreSQL or custom adapters via
ReportStore(see Storage Adapters) - Simple deployment —
npm run build && npm start
Repository layout
snapline-hub/
├── server/ # Express API (TypeScript)
│ ├── index.ts # App entry, static file serving
│ ├── routes.ts # REST route handlers
│ ├── db.ts # Storage factory re-exports
│ ├── storage/ # ReportStore adapters (sqlite, postgres, custom)
│ └── api.test.ts # API integration tests
├── web/ # React UI (Vite)
│ ├── src/
│ │ ├── pages/ # Dashboard, Reports, Detail, Upload
│ │ ├── api.ts # Fetch client
│ │ └── App.tsx # Router + layout
│ └── vite.config.ts
├── shared/
│ └── types.ts # TestRunReport types (shared server + web)
├── docs/ # GitHub Pages documentation (this site)
├── data/ # SQLite database (gitignored, created at runtime)
└── package.json
Server components
| Module | Responsibility |
|---|---|
server/index.ts | Creates Express app, mounts API router, serves web/dist in production |
server/routes.ts | REST endpoints: health, stats, reports CRUD |
server/db.ts | Storage factory; re-exports validation helpers |
server/storage/ | ReportStore interface, SQLite/PostgreSQL adapters, query builders |
shared/types.ts | TypeScript interfaces aligned with Snapline core |
Database schema
Single table reports — default backend is SQLite (WAL mode). PostgreSQL uses the same layout. See Storage Adapters for other backends.
| Column | Type | Description |
|---|---|---|
id | TEXT PK | UUID v4 |
generated_at | TEXT | From report.generatedAt |
framework | TEXT | From report.framework |
label | TEXT NULL | Display name (push/upload only) |
total | INTEGER | summary.total |
passed | INTEGER | summary.passed |
failed | INTEGER | summary.failed |
duration_ms | INTEGER NULL | summary.durationMs |
environment | TEXT NULL | JSON-serialized environment object |
report_json | TEXT | Full TestRunReport JSON |
created_at | TEXT | Server ingest timestamp |
Indexed on generated_at DESC and framework for list queries.
Data model
Hub stores Snapline's standard report hierarchy:
TestRunReport
├── generatedAt: string
├── framework: string
├── summary: { total, passed, failed, durationMs? }
├── environment?: Record<string, string>
└── suites: TestSuiteResult[]
├── name: string
├── passed: boolean
└── results: TestStepResult[]
├── step: string
├── passed: boolean
├── message?: string
├── diff?: DiffResult
├── processed?, source?, target?, data?
└── ...
Framework identifiers
| Runtime | framework value |
|---|---|
| Node.js | @vaagatech/snapline-engine |
| Python | snapline-engine |
Web UI
React 19 SPA with React Router. Pages fetch from /api/*:
| Page | API calls |
|---|---|
| Dashboard | GET /api/stats |
| All Runs | GET /api/reports |
| Run Detail | GET /api/reports/:id, DELETE |
| Upload | POST /api/reports |
Development vs production
| Mode | UI | API | How |
|---|---|---|---|
| Development | :5173 (Vite) | :3847 | Vite proxies /api to backend |
| Production | :3847 | :3847 | Express serves web/dist + API |
Integration with Snapline
Hub does not import Snapline packages. Integration is HTTP-only:
- Snapline test runner produces
TestSuiteResult[] buildReport()wraps intoTestRunReportpushTestReportToHub()POSTs JSON to Hub- Hub validates, stores in SQLite, UI reads via GET
This loose coupling means Hub can be upgraded independently of Snapline framework versions, as long as the TestRunReport schema remains compatible.
What Hub does not do
- Run tests — Hub only stores and displays results
- Stream JSONL — warehouse row-level JSONL streams stay in local files; Hub accepts batch
TestRunReportonly - Authenticate users — deploy behind your own auth layer
- Replace CI — exit codes and pass/fail gates remain in your test runner
- Replace local reports —
writeTestReportHTML/JSON/text files still work without Hub
Security considerations
| Topic | Guidance |
|---|---|
| Network exposure | Run on internal network or behind authenticated reverse proxy |
| Sensitive data | Redact before push using Snapline redactSuiteResults |
| Database file | Contains full test payloads — restrict filesystem permissions |
| Deletion | DELETE endpoint has no auth — protect at network layer |
| Request size | 50 MB JSON limit — avoid pushing unbounded payloads |
Scaling
Hub is designed for team-scale usage (hundreds to low thousands of reports). For very high volume:
- Archive old reports via DELETE or database backup rotation
- Run multiple Hub instances with separate databases per team/project
- Future: Postgres backend could replace SQLite (not implemented today)