stableFor developers

Project Configuration File (.pixmoat.yaml)

Manage project-level visual regression settings as code by checking a .pixmoat.yaml file into your repository root. Settings become PR-reviewable, reproducible across projects, and self-documenting per build.

Overview

The config file controls two categories of settings:

CategoryWhen appliedMechanism
Build-scopedEvery build, from the commit being testedCapture client reads the file and sends settings with POST /api/builds
Project-globalOnly from the default branchpixmoat config apply CLI command writes to the project via PUT /api/projects/:slug

Settings not in the file (secrets, org membership, instance config) remain in the UI or environment variables.

The feature is fully opt-in. Projects without a .pixmoat.yaml file behave exactly as before.

Quick start

  1. Create .pixmoat.yaml at your repository root:
# yaml-language-server: $schema=https://pixmoat.com/schemas/pixmoat-config.schema.json
version: 1
default_tolerance: 0.001
blocking_mode: blocking
  1. Commit and push. The capture client reads the file automatically on the next build.

  2. (Optional) Apply project-global settings from your default branch:

export PIXMOAT_URL=https://app.pixmoat.com
export PIXMOAT_PROJECT=my-project
export PIXMOAT_TOKEN=pxg_...
pixmoat config apply

Full example

# yaml-language-server: $schema=https://pixmoat.com/schemas/pixmoat-config.schema.json
version: 1                        # schema version — must be 1

# ── Build-scoped settings ─────────────────────────────────────────────
default_tolerance: 0.001           # 0.0–1.0, mismatch ratio threshold
blocking_mode: blocking            # blocking | report_only

diff:
  threshold: 0.1                   # YIQ color sensitivity (0.0–1.0)
  detect_aa: true                  # anti-aliasing detection
  diff_color: [255, 0, 255, 255]   # RGBA highlight color
  dpr: 1                           # device pixel ratio (1, 2, or 3)

viewports:                         # named viewport presets
  desktop: 1280x720
  tablet: 768x1024
  mobile: 375x812

browsers:                          # expected browser identifiers
  - chromium
  - firefox
  - webkit

flakiness:
  captures: 2                      # captures per snapshot (1–5)
  flakiness_threshold: 0.002       # inter-capture mismatch ratio
  quarantine_after: 3              # flaky builds before quarantine
  recovery_after: 5                # stable builds before recovery

performance:
  enabled: true
  gating: advisory                 # advisory | blocking
  regression_threshold: 0.15       # allowed variance from baseline (0.0–1.0)
  thresholds:
    lcp:  { soft_fail: 2500, hard_fail: 4000 }
    cls:  { soft_fail: 0.10, hard_fail: 0.25 }
    inp:  { soft_fail: 200,  hard_fail: 500  }
    fcp:  { soft_fail: 1800, hard_fail: 3000 }
    ttfb: { soft_fail: 800,  hard_fail: 1800 }
    total_transfer_size: { soft_fail: 2000000, hard_fail: 5000000 }
    total_js_size:       { soft_fail: 500000,  hard_fail: 1000000 }

removal_gating: advisory           # advisory | blocking

# ── Project-global settings (applied only from default branch) ────────
retention:
  keep_builds: 50                  # keep N most recent builds per branch
  delete_after_days: 90            # delete builds older than N days
  branch_rules:                    # per-branch overrides
    - branch: "release/*"
      keep_builds: 100
      delete_after_days: 0         # use plan maximum; unlimited only on an unlimited plan

agent_auto_approve: intent_only    # none | intent_only | all

webhook:
  url: https://ci.example.com/hook
  provider: gitlab                 # generic | github | gitlab
  on_approved: true
  on_rejected: true
  # secret: set via UI only — never in this file

Settings reference

Build-scoped settings

These travel with the commit and are evaluated per-build. Each build stores a snapshot of its resolved config, so you can always inspect what settings were in effect.

FieldTypeDefaultDescription
default_tolerancenumber0.001Mismatch ratio threshold (0.0–1.0). Per-snapshot overrides in test code take precedence.
blocking_modestringblockingblocking or report_only. Whether visual differences block the pipeline.
diff.thresholdnumber0.1YIQ color sensitivity (0.0–1.0). Lower = more sensitive.
diff.detect_aabooleantrueAnti-aliasing detection.
diff.diff_color[R,G,B,A][255,0,255,255]RGBA highlight color for changed pixels.
diff.dprinteger1Device pixel ratio (1, 2, or 3).
viewportsmapNamed viewport presets. Values must match WIDTHxHEIGHT (e.g. 1280x720).
browsersstring[]Expected browser identifiers: chromium, firefox, webkit.
flakiness.capturesinteger2Captures per snapshot (1–5).
flakiness.flakiness_thresholdnumber0.002Inter-capture mismatch ratio above which a snapshot is flaky.
flakiness.quarantine_afterinteger3Consecutive flaky builds before quarantine.
flakiness.recovery_afterinteger5Consecutive stable builds before recovery.
performance.enabledbooleanfalseEnable Core Web Vitals collection.
performance.gatingstringadvisoryadvisory or blocking.
performance.regression_thresholdnumber0.15Allowed variance from baseline (0.0–1.0).
performance.thresholds.*objectPer-metric { soft_fail, hard_fail } pairs. Metrics: lcp, cls, inp, fcp, ttfb, total_transfer_size, total_js_size.
removal_gatingstringadvisoryadvisory or blocking. How removed screenshots gate the review.

Project-global settings

These are applied only from the default branch via pixmoat config apply. Feature branches cannot change these settings.

FieldTypeDefaultDescription
retention.keep_buildsinteger50Keep N most recent builds per branch.
retention.delete_after_daysinteger90Delete builds older than N days. On a finite plan, 0 means use the plan maximum and values above the plan limit are clamped.
retention.branch_rulesarray[]Per-branch retention overrides. Each entry has branch (glob pattern), keep_builds, and delete_after_days; age values are also clamped by the plan.
agent_auto_approvestringintent_onlyAI agent auto-approve mode: none, intent_only, or all.
webhook.urlstringWebhook endpoint URL.
webhook.providerstringgenericgeneric, github, or gitlab.
webhook.on_approvedbooleantrueSend webhook on build approval.
webhook.on_rejectedbooleantrueSend webhook on build rejection.

Excluded settings (never in this file)

SettingReason
webhook.secretSecret — set via UI or environment variable
GitLab PAT / tokensSecret
Project access tokensSecret
Org membership, roles, billingOrg-scoped, not project-scoped
Instance/admin settingsOperator-scoped (pixmoat.toml)

The parser rejects the file with a clear error if any known secret field is present.

Precedence

Settings are resolved most-specific-wins:

per-snapshot override (in test code via SnapshotOptions)
  > .pixmoat.yaml build-scoped defaults
    > project DB settings (from last "pixmoat config apply" or UI edit)
      > server defaults

Plan quotas always clamp: retention cannot exceed your plan’s limits.

CLI commands

All commands use the same environment variables as the capture client (PIXMOAT_URL, PIXMOAT_PROJECT, PIXMOAT_TOKEN).

pixmoat config validate

Parse and validate .pixmoat.yaml without contacting the server.

pixmoat config validate                    # validates .pixmoat.yaml in CWD
pixmoat config validate --file custom.yaml # validates a specific file
pixmoat config validate --json             # structured JSON output

Exit codes: 0 = valid, 1 = invalid or not found.

pixmoat config diff

Compare the local file against the project’s current settings in the database.

pixmoat config diff

Output format:

~ default_tolerance: 0.001 → 0.005
~ diff.threshold:    0.1   → 0.15
+ viewports.tablet:  768x1024
  (3 changes)

Exit codes: 0 = file matches DB, 1 = differences found.

pixmoat config apply

Validate the file, show the diff, and write project-global settings to the project.

pixmoat config apply          # gated to default branch
pixmoat config apply --force  # bypass branch check (local dev only)

Branch gating: the command detects the current branch from $CI_COMMIT_BRANCH (GitLab), $GITHUB_REF_NAME (GitHub), or git rev-parse --abbrev-ref HEAD, and compares it to the project’s default_branch. The apply is rejected if they don’t match (unless --force is used).

Exit codes: 0 = applied, 1 = validation error / auth error / branch gate rejection.

CI integration

GitLab CI

pixmoat-test:
  script:
    - npx playwright test                  # capture client reads .pixmoat.yaml
    - pixmoat check --wait

pixmoat-config:
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  script:
    - pixmoat config validate
    - pixmoat config apply

GitHub Actions

jobs:
  pixmoat-test:
    steps:
      - uses: actions/checkout@v4
      - run: npx playwright test
      - run: pixmoat check --wait

  pixmoat-config:
    if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
    steps:
      - uses: actions/checkout@v4
      - run: pixmoat config validate
      - run: pixmoat config apply

Editor support

The JSON Schema at docs/schemas/pixmoat-config.schema.json enables autocompletion and validation in editors that support YAML Language Server.

Add this comment to the top of your .pixmoat.yaml:

# yaml-language-server: $schema=https://pixmoat.com/schemas/pixmoat-config.schema.json

VS Code users: install the YAML extension for automatic schema validation.

UI behavior

When a project’s most recent default-branch build included a .pixmoat.yaml file, the settings page shows a banner:

These settings are managed by .pixmoat.yaml. Changes made here will be overwritten on the next pixmoat config apply.

Settings remain editable in the UI for quick fixes and experimentation. The banner makes the consequence clear: the next pixmoat config apply from CI will reconcile settings back to the file’s values.

Backwards compatibility

  • Projects without a .pixmoat.yaml file are completely unaffected.
  • Builds created before this feature have NULL for resolved_config and fall back to reading settings from the project, identical to previous behavior.
  • The config field in the build creation API is optional. Omitting it preserves existing behavior.
  • Unknown top-level keys in the YAML file produce warnings, not errors, for forward compatibility.