| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| 6.0.0 source code.tar.gz | 2026-11-12 | 14.8 MB | |
| 6.0.0 source code.zip | 2026-11-12 | 14.8 MB | |
| README.md | 2026-11-12 | 14.0 kB | |
| Totals: 3 Items | 29.6 MB | 0 | |
VRT 6.0 flags rendering noise, lets you undo a decision and comes with a redesigned UI.
It is also the most secure VRT so far.
[!IMPORTANT] Upgrading from 5.x? The database moves to PostgreSQL 18. UI and API are upgraded together. Read Breaking changes, then follow Upgrade steps.
Highlights
Redesign
A new UI in light and dark, built around your projects.
- Project page with tabs: Builds, Variations, Statistics, Settings.
- Run list: filter chips, table or grid, bulk actions, comments at a glance.
- Run dialog: Side by side, Diff and Fade, plus edit mode for ignore areas.
- Undo approval and Undo rejection, also from the toast after bulk actions.
- Favourites: star a project to pin it to the top of the list and the switcher.
- Statistics: results per build, with a branch filter.
- Users page: search, role filter, joined date.
- Dark mode follows your system until you pick one.
![]()
Performance
Image comparison no longer blocks the API.
- Diffs run in a worker pool, off the event loop.
- The pool has CPUs − 1 workers (1 to 8) and queues up to 256 jobs.
- A full queue answers
503instead of stalling. - Three new indexes, built without blocking writes, speed up large projects.
- Nightly cleanup runs once and ages branches by last use.
Security
Secure by default.
JWT_SECRETis required; the public sample key is refused.- Live updates need a login and reach only people viewing that project or build.
- Containers run unprivileged: uid 1000, dinov2 as
nobody. REGISTRATION_ENABLED=falseturns off sign-up.- A leak of approvers' API keys and password hashes is closed (details).
Noise
Review the real changes first.
- Run Analyse noise from the build menu, the selection bar or a run's noise chip.
- Each unresolved run gets a verdict: Likely change or Likely noise.
- It is a hint: it never changes a status and never approves anything.
- Off by default; switch it on in project Settings → Post-analysis.
- Stage one is blur, on the API server.
- Stage two is optional DINOv2: image
visualregressiontracker/dinov2, compose profiledinov2. - Point the API at it with
DINOV2_URL; start it withdocker compose --profile dinov2 up -d.
![]()
Upgrade in one sentence
Dump PostgreSQL 12, restore into PostgreSQL 18, set JWT_SECRET, then start 6.0.
Container versions
:::env
VRT_UI_VERSION=6.0.0
VRT_API_VERSION=6.0.0
VRT_MIGRATION_VERSION=6.0.0
VRT_DINOV2_VERSION=6.0.0 # optional, profile "dinov2"
New optional settings in .env, commented out with their defaults:
REGISTRATION_ENABLED,LOG_LEVEL,MAX_IMAGE_PIXELS,MULTIPART_FILE_SIZE_LIMITDINOV2_URLand the otherDINOV2_*REDIS_URL: needed with more than one API instance, so live updates reach every client
Read by the API but not passed by docker-compose.yml; add them to its api environment:
HTTPS_KEY_PATH,HTTPS_CERT_PATHDIFF_WORKER_MAX_HEAP_MB,SERVER_HEADERS_TIMEOUT
See Configuration.
New
Compose and install
postgres:18; theapistarts aftermigrationhas finished.- Optional
dinov2service (profiledinov2). vrt-install.shuses Compose v2 and creates./imageUploadsfor uid 1000.- User docs live in the wiki, linked from the Help menu.
UI
- Variations: filters and the baseline history of each screen.
- Add project page, sectioned project settings, profile page with API key regenerate.
- Build header shows status counters.
- Project list: press
/to search. - Undoing a feature-branch approval restores that branch's ignore areas and comment.
API
GET /projects/favorites,PUT|DELETE /projects/:id/favoritePOST /test-runs/revert:204, or409if the status changedPOST /test-variations/merge: merge branchesGET /statistics/results: run counts per buildGET /dinov2/status- User summaries include
createdAt. - Bulk approve, reject and delete report the result of each run.
Breaking changes
Install and runtime
- PostgreSQL 18 cannot read a PostgreSQL 12 data directory: dump and restore once.
- An external database is not affected; 12 still works but is end of life, so move to 15+.
- The API and
migrationrun as uid 1000. On Linux,chown./imageUploads(and mounted TLS files). - Without it the API stops with
EACCES. - The image command is now
node dist/mainundertini; check it if you overridecommandorentrypoint. JWT_SECRETis required; the API refuses the public sample value.- Everyone signs in again once; API keys keep working.
- UI and API ship together. UI 6.0 needs API 6.0.
- UI 5.x gets no live updates from API 6.0 and misreads its bulk results.
DIFF_WORKERS_COUNTandDIFF_QUEUE_LIMITare no longer read.- Under a CPU quota below the host's CPU count, the worker count cannot be lowered.
DIFF_WORKER_MAX_HEAP_MBstays.- Docker Compose v2 and Node 26.
Noise
- Only
blur.enabledanddinov2.enabledofnoiseAnalysisConfigare read. - Other thresholds are constants; old keys are accepted and ignored.
API
- Errors are 4xx, not
200/500: 400validation404missing409conflict413too large422image overMAX_IMAGE_PIXELS(100 MP)503diff queue full- Check clients that treat any
2xxas success. - Error bodies are
{ statusCode, path, name, message }. /healthreturns503without the database.- Stricter uploads:
- Non-PNG screenshots get
400. GET /images/:namerefuses unexpected names with400.- Multipart uploads are capped at
MULTIPART_FILE_SIZE_LIMIT(100 MB). - Live updates (socket.io):
- Send
auth: { token }andjointhe project or build room. testRun_*payloads areTestRunDto.- A socket closes when its token expires.
- Removed endpoints:
GET /test-variations/merge: usePOSTwith{ projectId, fromBranch, toBranch }.PATCH /builds/:id/approve: usePOST /test-runs/approve.POST /ollama/compare;GET /ollama/modelsstays.PATCH /builds/:idrejects unknown fields.PATCH /test-runs/update/:idaccepts onlycomment; unknown fields are dropped everywhere.- Deleting users is all or nothing.
- The UI logs you out when the server closes the live connection.
Results and database
- Comparison results can change:
- The project's comparison config is applied over the defaults.
- pixelmatch treats different sizes as different.
- looks-same honours
diffTollerancePercent. - odiff is 4.5.0.
- TLS to PostgreSQL only when
DATABASE_URLasks (?sslmode=require). sslmode=prefernow meansrequire.pgbouncer,statement_cache_size,socket_timeout,sslidentity,sslpasswordare ignored (Prisma 7).- SSO:
OIDC_REDIRECT_URImust not contain a query string.
Removed
- The Merge branches panel (the API stays).
- The guided tour; the Help menu links to the documentation.
Clients
The 6.0.0 SDKs and agents are released for this server. They need newer runtimes; on older ones the latest 5.x releases keep working with 6.0 and handle its statuses and error codes.
| Client | 6.0 release | Runtime | Or, on an older runtime | Why that 5.x |
|---|---|---|---|---|
| sdk-js | 6.0.0 | Node.js 26 | 5.9.0 | sourceBranch; clear 404/409/413/422/503 messages; S3 image URLs |
| agent-cypress | 6.0.0 | Node.js 26 | 5.9.0 | sourceBranch / VRT_SOURCEBRANCH |
| agent-playwright | 6.0.0 | Node.js 26 | 5.5.0 | sourceBranch / VRT_SOURCEBRANCH |
| agent-codeceptjs | 6.0.0 | Node.js 26 | 5.0.0 | sdk-js 5, CodeceptJS 3.7 |
| sdk-java | 6.0.0 | Java 25 | 5.6.1 | stops builds correctly; branchName from the build |
| sdk-python | 6.0.0 | Python 3.15 | 5.1.0 | 4.9.0 and earlier crash on autoApproved; Python 3.10+ |
| sdk-dotnet | 6.0.0 | .NET 10 | 5.1.0 | .NET 10; stops builds correctly; image URLs |
baselineBranchNamenever had an effect; usesourceBranch(sdk-js 5.8.0+).- Screenshots over
BODY_PARSER_JSON_LIMIT(5 MB in.env) get413. - sdk-js and agent-cypress can upload them as multipart.
Security
[!WARNING] From api 4.16.0 to 5.7.0,
GET /test-variations/details/:idleaked approvers' API keys and password hashes. Any signed-in user, guests included, could read them. If untrusted users could sign in, change those users' passwords. Then regenerate their API keys (profile page → Regenerate).
GET /images/%2F…no longer redirects to another site.- Before 6.0, an editor's API key could rewrite a build's
branchName,number,statusorprojectId. /ollama/*needs an admin or editor login.GET /users/alland role changes no longer return other users' API keys.- The Gemini API key is masked in project responses.
.envstill ships sampleDEFAULT_USER_PASSWORDandDEFAULT_USER_API_KEY: change both before exposing VRT.
Fixed
- Comparison: different sizes, looks-same tolerance, odiff results, project config over defaults.
- Branches:
- Editing a feature-branch run no longer changes main's variation.
- Merging into an older variation works.
- Variations list in a stable order.
- Builds:
- An empty
PATCH /builds/:idstops the build again. maxBuildAllowednever removes the reused build.- VLM: ignore areas are masked in images sent to the model; stale VLM text is cleared.
- API:
POST /test-runsto a missing project returns404.- A run that cannot be compared is refused before it is stored.
- OIDC trusts the issuer the provider advertises.
- dinov2 answers
503/413pastDINOV2_MAX_QUEUE/DINOV2_MAX_PIXELS. - UI:
- Bulk actions act only on the selected runs.
- Ignore areas stay inside the image.
- Hotkeys and Escape work in the run dialog.
- Guests no longer see project edit controls.
- Tag filters search their own field.
Upgrade steps
Run in the VRT folder (Linux, macOS or WSL).
- Dump the database with UI and API stopped:
sh docker compose stop ui api docker compose exec -T postgres sh -c 'pg_dump -U "$POSTGRES_USER" -Fc "$POSTGRES_DB"' > vrt-pg12.dump docker compose down && mv db db-pg12 - Take
.envanddocker-compose.ymlfrom this release. - Keep your values; keep the old files for a rollback.
- Set
JWT_SECRET(openssl rand -hex 32). - Change
DEFAULT_USER_PASSWORDandDEFAULT_USER_API_KEYif they are the samples. - Restore into PostgreSQL 18:
sh docker compose up -d --wait postgres docker compose exec -T postgres sh -c 'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --exit-on-error' < vrt-pg12.dump - Linux only:
sudo chown -R 1000:1000 ./imageUploads(and your TLS files, if mounted). - Start:
docker compose pull && docker compose up -d. - The
migrationservice updates the schema before the API starts. - From 5.5 or later it applies four migrations: three concurrent indexes, then
upgrade_to_6_0_0. - Check that
docker compose logs migrationends with All migrations have been successfully applied. - Check that the Help menu shows 6.0.0.
- Delete
./db-pg12andvrt-pg12.dump.
Rollback:
docker compose downmv db db-new && mv db-pg12 db- Restore the old
.envanddocker-compose.yml. docker compose up -d
Data written on 6.0 is lost.
If an index build fails (P3009, the migration container keeps restarting)
A cancelled `CREATE INDEX CONCURRENTLY` (for example a `statement_timeout`) leaves an invalid index. 1. Find the invalid index: ```sh docker compose exec -T postgres sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "select indexrelid::regclass from pg_index where not indisvalid"' ``` 2. Drop it: ```sh docker compose exec -T postgres sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "DROP INDEX CONCURRENTLY \"TestRun_testVariationId_idx\""' ``` 3. Mark its migration as rolled back: ```sh docker compose run --rm --entrypoint npx migration \ prisma migrate resolve --rolled-back 20261024174953_add_test_run_test_variation_index ``` 4. Run the migrations again: `docker compose up -d migration api` | Index | Migration | |---|---| | `TestRun_testVariationId_idx` | `20261024174953_add_test_run_test_variation_index` | | `Baseline_testVariationId_createdAt_idx` | `20261024175021_add_baseline_test_variation_index` | | `TestRun_name_baselineName_idx` | `20261024175038_add_test_run_name_baseline_name_index` |PostgreSQL
- Bundled database: PostgreSQL 18, volume
./db:/var/lib/postgresql. - Your own PostgreSQL 12 still works but is end of life; move to 15+.
- Only the bundled
postgresservice needs the dump and restore above.
Component releases
- API, migration and DINOv2: backend api-v6.0.0 (also migration-v6.0.0, dinov2-v6.0.0)
- UI: frontend v6.0.0
Full Changelog: https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker/compare/5.9.0...6.0.0