Skip to content
decosa
LiveHostedSelf-host

Find accessibility fixes for a shop

A ranked fix list of accessibility problems that stop shoppers buying, each with its WCAG criterion, the element and a code fix.

Held-out test122 / 127Planted accessibility problems caught (held-out test)
On production1.8 smedian on production (2026-09-28); slower when the service is busy
List price~$0.21 per 100 pagesmeasured, at list price

Built on: Signed record

Loading the tool…

Use it your way

Use it from your codeThe hosted API with your key, and prompts to paste into a coding agent
Hosted · by Decosa

Get an API key

  • Call the storefront accessibility pass API from your own code in minutes.
  • Every model answer carries a signed receipt.
  • Nothing to install; we run the models.
Self-host · your GPUs

Run it yourself, on request

  • The same open models and app, on 1x RTX PRO 6000 (96 GB) for Qwen3.8-27B with its vision tower; headless Chromium and axe-core on CPU.
  • Data never leaves your machines, and there are no Decosa charges.
  • One prompt for Claude Code or Codex assembles the whole stack.
  • Early access: the container images are not public yet and the source needs access; the prompt says how to ask.

Build with it

Paste one of these into Claude Code, Codex or another coding agent. The first wires your project to the hosted API with your DECOSA_API_KEY. The second pulls our containers and runs the same stack on your own GPU, with no Decosa charges.

Base URL
https://api.decosa.ai
Auth
Authorization: Bearer $DECOSA_API_KEY (or a demo session token)
Tool id
storefront-accessibility-pass

Use the hosted API

# Decosa storefront accessibility pass: use the hosted API

You are wiring Decosa's storefront accessibility pass into this project (a shop's release checklist, an agency's QA
script or CI). For up to five pages of one shop it runs axe-core's automated WCAG 2.2 A and AA rules in a private
headless Chromium, tries add-to-cart and the checkout with the keyboard alone, submits empty forms to read the error
messages, and asks Qwen3.8-27B with vision what a rule engine cannot (is the alt text right for this image, are link and
button names clear, does an error say how to fix the field, is an offer only inside an image). It returns a fix list
ranked by what blocks a purchase, each finding with its WCAG success criterion and a code fix, and a signed record.
Every model call has a signed receipt. Use only what is listed below; if you need something else, stop and ask me.

- Base URL: `https://api.decosa.ai`
- Health check: `GET https://api.decosa.ai/healthz`.
- It is an audit aid, not a certification and not a statement that a site conforms to WCAG, the ADA or the European
  Accessibility Act. A person reviews every finding. Not legal advice.
- Only audit a site you own or have permission to test. Live URLs need an API key and a domain you verified (below).
  The browser only sends GET and HEAD requests, so it never submits an order or a form to your server.

## Auth: API key (or a demo session)
1. Preferred: an API key (`dk_…`) from "Get an API key" on the tool page. Keep it in `DECOSA_API_KEY`, never in code.
   Send `Authorization: Bearer $DECOSA_API_KEY`.
2. Without a key: `POST https://api.decosa.ai/demo/session` with `{"vertical": "storefront-accessibility-pass"}` returns
   `{"token", "expires_at", "budget"}`. A demo session can audit the sample shop and pasted HTML only (403 for URLs).
   Over a limit you get HTTP 429 with `Retry-After`; 402 when the budget left is too small (about 4,200 generated
   tokens per page with the model on).

## Verify your domain (once per key)
- `POST /a11y/domains {"domain": "shop.example.com"}` returns a token. Publish it as a DNS TXT record at
  `_decosa-challenge.shop.example.com`, or at `https://shop.example.com/.well-known/decosa-verify.txt`, then
  `POST /a11y/domains/check {"domain": "shop.example.com"}`. `GET /a11y/domains` lists yours. A domain verified for test
  runs with the same key is already covered.

## Run an audit
- `POST /a11y/audit` (token or key). Body, exactly one of:
  - `{"urls": ["https://shop.example.com/", "https://shop.example.com/products/x", "https://shop.example.com/cart"]}`
    (1 to 5 URLs on one verified origin);
  - `{"html": "<!doctype html>...", "synthetic": true}` (one page, ≤ 400,000 characters; the browser has no network, so
    inline images as `data:` URLs if you want them judged; `synthetic: true` confirms it holds no personal data);
  - `{"sample_id": "shop-with-issues" | "clean-shop" | "product-page"}` (Harbor & Pine, a made-up shop).
  - Options: `"judge": false` (rules, keyboard and form passes only, no model calls), `"mobile": true` (390x844),
    `"stream": true` (or `Accept: text/event-stream`).
- JSON response: `summary` (`findings`, `by_priority`, `by_source`, `advisories`, `wcag_criteria`, `pages`), `findings`
  (each: `category`, `priority` P1-P4, `title`, `detail`, `source` axe-core | keyboard pass | form pass | model, `wcag`
  [{sc, name, level, url}], `page`, `element.snippet`, `fix {how, code}`, `suggestion`, `receipt`), `advisories`,
  `checked` (axe-core version and sha256, pages, images judged, tab stops, forms submitted, prompt hashes),
  `not_checked`, `record`, `receipt_events`, `model_calls`, `elapsed_s`.
- SSE events: `start`, `page` (with a screenshot), `page_error`, `image`, `names`, `errors`, `receipt`, `finding`,
  `result`, `budget`, `done`.
- `POST /record/verify` (no token) `{"record": {...}}` re-checks the signed record.
- `GET /a11y/info` (laws with dates and links, WCAG criteria, limits, what it does not check) and `GET /a11y/samples`
  need no token.

## Example (Python, `pip install httpx`)
```python
import httpx, json, os, pathlib
API = "https://api.decosa.ai"
H = {"Authorization": f"Bearer {os.environ['DECOSA_API_KEY']}"}
r = httpx.post(f"{API}/a11y/audit", headers=H, timeout=600,
               json={"urls": ["https://shop.example.com/", "https://shop.example.com/products/x"], "stream": False})
r.raise_for_status()
js = r.json()
for f in js["findings"]:
    sc = ", ".join(w["sc"] for w in f["wcag"])
    print(f"{f['priority']}  {f['category']:<15} WCAG {sc:<12} {f['page']['url']}  {f['detail'][:100]}")
    print("     fix:", f["fix"]["code"][:160])
pathlib.Path("a11y-record.json").write_text(json.dumps(js["record"]))
```
In CI, fail the build when `summary.by_priority.P1 > 0`, and keep each record with the release.

Run it yourself (containers)

On request. The container images and the compose file aren’t public yet. Ask for self-host access and Decosa sends the registry (DECOSA_REGISTRY) and the compose file’s URL (DECOSA_COMPOSE_URL) these steps use. They are the steps we tested end to end on a fresh machine.

# Decosa storefront accessibility pass: run it yourself (containers)

You are setting up Decosa's storefront accessibility pass on this machine, so a staging shop, a site behind a login or
an unreleased theme never leaves it. It opens up to five pages in a private headless Chromium, runs axe-core's WCAG 2.2
A/AA rules, a keyboard pass over add-to-cart and the checkout and an empty-submit form pass, asks Qwen3.8-27B with vision
about alt text, link and button names, error messages and text in images, and writes a ranked fix list and a signed
record. Nothing is sent to Decosa's hosted API. It is an audit aid, not a certification; a person reviews the findings.

Status: the container images (${DECOSA_REGISTRY}/decosa-*) and the compose file are on request while self-host is in early access (not on a public registry yet): ask at https://decosa.ai/contact?topic=self-host, and Decosa sends the registry as DECOSA_REGISTRY, the compose file URL as DECOSA_COMPOSE_URL, and pull access. If a pull fails with
"not found", "denied" or "unauthorized", stop and tell me. Do not substitute other images.

Ask me before any command that needs sudo, and show me the command first.

## Step 0: set up with a coding agent, rehearse on mock data, then go private

This prompt is for a coding agent running on the machine that will host the service. We recommend Claude Code with
Claude Opus 5.5; any capable coding agent works. Work in this order:

1. Set up on mock data only. During the whole setup you (the agent) work with the synthetic sample bundle below and
   nothing else. Do not ask me for real data, and do not open, read, list or copy files that hold real data, even to
   "test with something realistic".
2. Rehearse. When the steps below are done and the service is healthy, fetch the mock-data bundle for this tool,
   https://decosa.ai/samples/storefront-accessibility-pass.zip (58 KB, 8 checks, synthetic or openly licensed: see `licence` in expected.json),
   show me what is in it, and run the rehearsal against the local API:
   `docker compose exec api python scripts/rehearse.py storefront-accessibility-pass` (the api image carries the same bundle under /app/rehearsal/storefront-accessibility-pass/;
   with no key set, the script asks the local API for a short demo token). From a decosa-api checkout instead:
   `python scripts/rehearse.py storefront-accessibility-pass --bundle storefront-accessibility-pass.zip --base-url http://127.0.0.1:<PORT>`.
   It sends the mock inputs to the local API and prints PASS or FAIL for each expected property (for example: "Add to cart, a div the keyboard cannot reach, is found and ranked first (P1)", "the quantity field without a label is found by the rule engine", "the packshot's alt text, which calls the orange can purple, is flagged by the model"). Show me
   the full output. Every check must pass. If one fails, fix the install and run it again; never edit `expected.json`
   to make a check pass.
3. Stop there. Once the rehearsal passes, tell me, and I will run my own data against the local API myself, on this
   machine.

For the person running this: a coding agent that runs in the cloud sees everything in its context, including files it
reads, command output and anything pasted into the chat. Keep real data out of the chat and out of anything the agent
can read. Switch to your own data only after the rehearsal has passed and the agent's work is done.

## Steps
1. Docker: if `docker compose version` fails, install Docker Engine and the compose plugin using Docker's official
   instructions for this distribution (docs.docker.com/engine/install). Install the NVIDIA container toolkit and check
   `docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi`.
2. Fetch the compose file:
   `mkdir -p ~/decosa && cd ~/decosa && curl -fsSL "${DECOSA_COMPOSE_URL}" -o compose.yaml`
   Read it. Keep the `llm` service (Qwen3.8-27B on vLLM, with its vision tower: no `--language-model-only`) and the `api`
   service. For the `api` service set `DECOSA_LLM_ROUTE=direct`, `DECOSA_LLM_URL=http://llm:8000/v1`,
   `DECOSA_LLM_MODEL=qwen3.8-27b`, `shm_size: 1gb`, and bind every port to 127.0.0.1.
3. The api image must include the headless browser: build it from the decosa-api source with
   `docker build -f docker/api/Dockerfile --build-arg WITH_BROWSER=1 .` if the pulled image reports
   `browser_available: false` at `GET /a11y/info`.
4. Your shop: set `DECOSA_TESTRUNS_TARGETS=<your staging host>` (comma-separated) on the api so it can be audited without
   DNS verification, and `DECOSA_TESTRUNS_ALLOW_PRIVATE=1` if it resolves to a private address. Only list sites you
   own or may test.
5. Pull and start: `docker compose pull && docker compose up -d`. Wait for the `llm` health check.
6. Check: `curl -fsS http://127.0.0.1:<PORT>/a11y/info` shows `browser_available: true` and axe-core `4.13.0` (MPL-2.0);
   `GET /attest/signing-key` shows this box's public key.
7. Smoke test: get a token with `POST /demo/session {"vertical":"storefront-accessibility-pass"}` and send
   `{"sample_id": "shop-with-issues", "stream": false}` to `POST /a11y/audit`. Expect P1 findings for Add to cart (a div
   Tab never reaches) and the newsletter dialog (a keyboard trap), plus model findings for the purple-can alt text and
   the offer only in the banner. Then `{"sample_id": "clean-shop"}` should give no findings. Send the first record to
   `POST /record/verify`: `ok` must be true.
8. Report back: the public key, the finding counts by priority, and how long each run took.

Off by default. Joining as a provider serves other people's requests on this GPU; audits never need it. If I ask for it
later, follow the Provide page instead of improvising.
Run it on your own hardwareWhat it needs, and the prompt that sets it up

Run it on your own GPU

Same app, same pinned models, your hardware. Nothing goes to our servers and there are no Decosa charges.

  • CPU only, 64 GB RAMlite tierRuns with a smaller tier

    The standard tier does not fit: Qwen3.8-27B (NVIDIA NVFP4) needs a GPU. The lite tier fits.

  • GeForce RTX 4090standard tierRuns

    The standard tier fits with changes: Replace Qwen3.8-27B (NVIDIA NVFP4) with A community 4-bit build of Qwen3.8-27B (AWQ or GGUF). This build is NVIDIA NVFP4, which needs a Blackwell GPU. (Memory is an estimate.)

  • GeForce RTX 5090standard tierRuns

    The standard tier fits with changes: Qwen3.8-27B (NVIDIA NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions.

  • 2x GeForce RTX 5090standard tierRuns

    The standard tier fits with changes: Split the language model across the GPUs with tensor parallelism (vLLM --tensor-parallel-size).

  • L40Sstandard tierRuns

    The standard tier fits with changes: Replace Qwen3.8-27B (NVIDIA NVFP4) with Qwen3.8-27B official FP8. This build is NVIDIA NVFP4, which needs a Blackwell GPU.

  • H100 80 GB (SXM)standard tierRuns

    The standard tier fits with changes: Replace Qwen3.8-27B (NVIDIA NVFP4) with Qwen3.8-27B official FP8. This build is NVIDIA NVFP4, which needs a Blackwell GPU.

  • RTX PRO 6000 Blackwell 96 GBstandard tierRuns

    The standard tier fits (57.6 of 96 GB).

  • 2x RTX PRO 6000 Blackwell 96 GBstandard tierRuns

    The standard tier fits (57.6 of 192 GB).

  • Apple M3 Ultra (Mac Studio), 96 GBstandard tierRuns

    The standard tier fits with changes: Replace Qwen3.8-27B (NVIDIA NVFP4) with Qwen3.8-27B MLX 4-bit. MLX build for Apple Silicon.

  • Apple M5 Max, 64 GBstandard tierRuns

    The standard tier fits with changes: Replace Qwen3.8-27B (NVIDIA NVFP4) with Qwen3.8-27B MLX 4-bit. MLX build for Apple Silicon.

Memory per component comes from measured footprints, the tool's stack.json, or an estimate from its parameter count, and each is labelled that way below. Only an RTX PRO 6000 and an M3 Ultra Mac Studio have actually been run.

On request. The container images and the compose file aren’t public yet. Ask for self-host access and Decosa sends the registry (DECOSA_REGISTRY) and the compose file’s URL (DECOSA_COMPOSE_URL) these steps use. They are the steps we tested end to end on a fresh machine.

  1. 1

    Check the GPU, Docker and the NVIDIA Container Toolkit

    The driver must see the GPU, and Docker must be able to pass it into a container.

    nvidia-smi
    docker compose version
    docker run --rm --gpus all ubuntu nvidia-smi
  2. 2

    Fetch the compose file

    One file describes the API and the language model as services.

    mkdir -p ~/decosa && cd ~/decosa
    curl -fsSL "${DECOSA_COMPOSE_URL}" -o compose.yaml
  3. 3

    Pull and start

    The first start downloads pinned model weights, tens of gigabytes.

    docker compose pull
    docker compose up -d
  4. 4

    Check health

    Wait until the API reports ok with the language model loaded. Then point your app at the local base URL.

    curl -fsS http://localhost:<PORT>/healthz
    # {"ok": true, "llm": true, ...}
    curl -fsS -X POST http://localhost:<PORT>/demo/session \
      -H 'Content-Type: application/json' -d '{"vertical":"storefront-accessibility-pass"}'

Set up with a coding agent, rehearse on mock data, then go private

  1. Set up with a coding agent. Paste the self-host prompt into a coding agent on the machine that will run the service. We recommend Claude Code with Claude Opus 5.5; any capable coding agent works.
  2. Rehearse on mock data. The agent runs the tool on a bundle of synthetic inputs and checks each answer against the bundle's expected.json. Every check must print PASS.
  3. Go private. Only then do you run your own data against the local API, yourself, on that machine. Never give the agent real data during setup: a coding agent that runs in the cloud sees everything in its context, so keep real data out of the chat and out of the files it reads.
Rehearsal command
docker compose exec api python scripts/rehearse.py storefront-accessibility-pass

Download the mock-data bundle (58 KB, 8 checks)expected.json

A product page from Harbor & Pine, a made-up shop, pasted as HTML with its images inline (the browser has no network for pasted pages). Four issues are planted: the packshot's alt says the can is purple (it is orange), Add to cart is a div the keyboard cannot reach, the quantity field has no label, and a banner offer exists only inside the image. The audit must find the rule-engine issue, the keyboard blocker (as P1) and the two model-judged ones, attach a receipt to every model call, and sign a record that verifies. Then the clean version of the sample shop, without the model, must come back with nothing found.

What the rehearsal checks
  • Add to cart, a div the keyboard cannot reach, is found and ranked first (P1)
  • the quantity field without a label is found by the rule engine
  • the packshot's alt text, which calls the orange can purple, is flagged by the model
  • the banner offer that exists only in the image is flagged
  • at least four findings, each with its WCAG references
  • every model call has a receipt
  • the signed record verifies
  • the clean sample shop, checked without the model, has no findings

Licence: Harbor & Pine is invented for decosa-api (AGPL-3.0-or-later): packshots drawn with Pillow, the banner photo drawn by Wan2.2-VACE-Fun-A14B (Apache-2.0).

Prompt for your coding agent

# Decosa storefront accessibility pass: run it yourself (containers)

You are setting up Decosa's storefront accessibility pass on this machine, so a staging shop, a site behind a login or
an unreleased theme never leaves it. It opens up to five pages in a private headless Chromium, runs axe-core's WCAG 2.2
A/AA rules, a keyboard pass over add-to-cart and the checkout and an empty-submit form pass, asks Qwen3.8-27B with vision
about alt text, link and button names, error messages and text in images, and writes a ranked fix list and a signed
record. Nothing is sent to Decosa's hosted API. It is an audit aid, not a certification; a person reviews the findings.

Status: the container images (${DECOSA_REGISTRY}/decosa-*) and the compose file are on request while self-host is in early access (not on a public registry yet): ask at https://decosa.ai/contact?topic=self-host, and Decosa sends the registry as DECOSA_REGISTRY, the compose file URL as DECOSA_COMPOSE_URL, and pull access. If a pull fails with
"not found", "denied" or "unauthorized", stop and tell me. Do not substitute other images.

Ask me before any command that needs sudo, and show me the command first.

## Step 0: set up with a coding agent, rehearse on mock data, then go private

This prompt is for a coding agent running on the machine that will host the service. We recommend Claude Code with
Claude Opus 5.5; any capable coding agent works. Work in this order:

1. Set up on mock data only. During the whole setup you (the agent) work with the synthetic sample bundle below and
   nothing else. Do not ask me for real data, and do not open, read, list or copy files that hold real data, even to
   "test with something realistic".
2. Rehearse. When the steps below are done and the service is healthy, fetch the mock-data bundle for this tool,
   https://decosa.ai/samples/storefront-accessibility-pass.zip (58 KB, 8 checks, synthetic or openly licensed: see `licence` in expected.json),
   show me what is in it, and run the rehearsal against the local API:
   `docker compose exec api python scripts/rehearse.py storefront-accessibility-pass` (the api image carries the same bundle under /app/rehearsal/storefront-accessibility-pass/;
   with no key set, the script asks the local API for a short demo token). From a decosa-api checkout instead:
   `python scripts/rehearse.py storefront-accessibility-pass --bundle storefront-accessibility-pass.zip --base-url http://127.0.0.1:<PORT>`.
   It sends the mock inputs to the local API and prints PASS or FAIL for each expected property (for example: "Add to cart, a div the keyboard cannot reach, is found and ranked first (P1)", "the quantity field without a label is found by the rule engine", "the packshot's alt text, which calls the orange can purple, is flagged by the model"). Show me
   the full output. Every check must pass. If one fails, fix the install and run it again; never edit `expected.json`
   to make a check pass.
3. Stop there. Once the rehearsal passes, tell me, and I will run my own data against the local API myself, on this
   machine.

For the person running this: a coding agent that runs in the cloud sees everything in its context, including files it
reads, command output and anything pasted into the chat. Keep real data out of the chat and out of anything the agent
can read. Switch to your own data only after the rehearsal has passed and the agent's work is done.

## Steps
1. Docker: if `docker compose version` fails, install Docker Engine and the compose plugin using Docker's official
   instructions for this distribution (docs.docker.com/engine/install). Install the NVIDIA container toolkit and check
   `docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi`.
2. Fetch the compose file:
   `mkdir -p ~/decosa && cd ~/decosa && curl -fsSL "${DECOSA_COMPOSE_URL}" -o compose.yaml`
   Read it. Keep the `llm` service (Qwen3.8-27B on vLLM, with its vision tower: no `--language-model-only`) and the `api`
   service. For the `api` service set `DECOSA_LLM_ROUTE=direct`, `DECOSA_LLM_URL=http://llm:8000/v1`,
   `DECOSA_LLM_MODEL=qwen3.8-27b`, `shm_size: 1gb`, and bind every port to 127.0.0.1.
3. The api image must include the headless browser: build it from the decosa-api source with
   `docker build -f docker/api/Dockerfile --build-arg WITH_BROWSER=1 .` if the pulled image reports
   `browser_available: false` at `GET /a11y/info`.
4. Your shop: set `DECOSA_TESTRUNS_TARGETS=<your staging host>` (comma-separated) on the api so it can be audited without
   DNS verification, and `DECOSA_TESTRUNS_ALLOW_PRIVATE=1` if it resolves to a private address. Only list sites you
   own or may test.
5. Pull and start: `docker compose pull && docker compose up -d`. Wait for the `llm` health check.
6. Check: `curl -fsS http://127.0.0.1:<PORT>/a11y/info` shows `browser_available: true` and axe-core `4.13.0` (MPL-2.0);
   `GET /attest/signing-key` shows this box's public key.
7. Smoke test: get a token with `POST /demo/session {"vertical":"storefront-accessibility-pass"}` and send
   `{"sample_id": "shop-with-issues", "stream": false}` to `POST /a11y/audit`. Expect P1 findings for Add to cart (a div
   Tab never reaches) and the newsletter dialog (a keyboard trap), plus model findings for the purple-can alt text and
   the offer only in the banner. Then `{"sample_id": "clean-shop"}` should give no findings. Send the first record to
   `POST /record/verify`: `ok` must be true.
8. Report back: the public key, the finding counts by priority, and how long each run took.

Off by default. Joining as a provider serves other people's requests on this GPU; audits never need it. If I ask for it
later, follow the Provide page instead of improvising.

Help me customise for my hardware

Pick your GPU or Mac, or enter its memory. You get the tier that fits, the model swaps it needs, measured speed where we have it, and a setup prompt with those choices written in.

Hardware

GeForce RTX 5090: 32 GB GDDR7, 1,792 GB/s, FP8 and NVFP4. NVIDIA product page

RunsStorefront accessibility pass on GeForce RTX 5090: use the Standard · rules, keyboard, forms and the model (hosted demo) tier

The standard tier fits with changes: Qwen3.8-27B (NVIDIA NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions.

Standard · rules, keyboard, forms and the model (hosted demo): what changesuses estimates

  • Qwen3.8-27B (NVIDIA NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions.
Memory per component
  • Looks at each image with its alt text: Qwen3.8-27B (NVIDIA NVFP4). ~57.6 GB (at least ~28 GB), weights 21.4 GB (from stack.json). Qwen3.8-27B NVFP4: Weights 19.9 GiB (21.4 GB), measured (field stack.json). The compose file gives the server 0.60 of a 96 GB card (57.6 GB) so the rest is FP8 KV cache for several sessions. The 28 GB minimum is an estimate: weights plus a short-context KV cache, which is why several stacks list a 32 GB RTX 5090 as 'estimate'. (stack.json lists 57 GB for this component.)
  • Opens each page in a private headless Chromium: axe-core 4.13.0 in headless Chromium (Playwright 1.58). CPU. Runs on CPU (vram_gb 0 in stack.json).
  • Merges the rule, keyboard, form and model res...: decosa-api a11y module (decosa_api/verticals/a11y). CPU. Runs on CPU (vram_gb 0 in stack.json).

Expected speed

Not measured.

Not measured on this hardware. The only measured setups are an RTX PRO 6000 Blackwell and a Mac Studio M3 Ultra.

Setup prompt for this hardware

The self-host prompt for Storefront accessibility pass, with a hardware plan for GeForce RTX 5090 added after Step 0. Loading the full prompt; until then it points your agent at the prompt's URL.

# Set up Storefront accessibility pass on my hardware

Fetch https://decosa.ai/prompts/storefront-accessibility-pass-selfhost.md and follow it (including Step 0: rehearse on mock data first), with the hardware plan below applied.

## Hardware plan for this machine (from https://decosa.ai/self-host/hardware?use=storefront-accessibility-pass)

Target machine: GeForce RTX 5090 (32 GB of GPU memory; CUDA, FP8 and NVFP4).
Quality tier: Standard · rules, keyboard, forms and the model (hosted demo) (standard). Fit check: runs with changes, about 28 GB of 32 GB used; some memory numbers are estimates, not measurements.

First, check the machine: run `nvidia-smi` (or `rocm-smi`, or `sysctl hw.memsize` on a Mac) and confirm the GPUs and free memory match the line above. If they do not, stop and tell me before pulling anything.

Use these components (the setup below describes the standard tier; change it to match):
- Looks at each image with its alt text: Qwen3.8-27B (NVIDIA NVFP4) (nvidia/Qwen3.8-27B-NVFP4), 57.6 GB. Change: Qwen3.8-27B (NVIDIA NVFP4): run it at its smallest setting (about 28 GB instead of 57.6 GB), with a shorter context and fewer parallel sessions.
- Opens each page in a private headless Chromium: axe-core 4.13.0 in headless Chromium (Playwright 1.58), CPU
- Merges the rule, keyboard, form and model res...: decosa-api a11y module (decosa_api/verticals/a11y), CPU

GPU placement (set each service's device and its vLLM --gpu-memory-utilization to about the share shown):
- GPU 0: Qwen3.8-27B (NVIDIA NVFP4) ~28 GB (88%); about 4 GB left

During the rehearsal, watch GPU memory. If a model fails to load or runs out of memory, lower its --max-model-len and --max-num-seqs first, then its memory share, and tell me what you changed.

The stack's own component list and compose layout: https://decosa.ai/prompts/storefront-accessibility-pass-assemble.md

The proof

How we tested itEval results and end-to-end checks, hosted and self-hosted, with dates

Verified end to end

Hosted: verified 28 Sep 2026 · measured 28 Sep 2026: · p50 1.8 s · p95 13 s (6 runs) · ~<$0.001 per run · 2 receipts

Loading the nightly status…

Self-host: verified 27 Sep 2026 · Fresh clone of the branch into a clean directory, docker build of the api image with WITH_BROWSER=1, the api on host networking against the running local Qwen3.8-27B (vision, direct route); then torn down.

Measured cost to run: about $0.21 per 100 pages (hosted, 28 Sep 2026). Self-hosting is free: the code is open and the models are open-weight. You pay only for your own hardware and power.

The rehearsal bundle passed 8 of 8 in 11 s; the three-page sample shop gave 13 findings (5 P1) and the clean shop none, receipts attested; a URL audit of a local http staging page (DECOSA_TESTRUNS_TARGETS + ALLOW_PRIVATE, 390 px) found its 6 issues with only GET requests reaching the server. The model server's own startup was not re-verified (no new GPU load).

Known limits (5)
  • Hosted numbers are from 6 production smoke runs after the merge: 3 on 27-28 Sep under load (10.7, 10.9, 13.3 s) and 3 on a quiet gateway on 28 Sep (1.8, 1.8, 1.8 s). Cost is the median at list price, model calls included (range $0.0004 to $0.0004).
  • Measured on one made-up shop template; real themes with lazy-loaded images, cookie banners and third-party widgets were not tested.
  • The model asks for an alt on a product photo used as a background behind real text; the page audit reports that as an advisory, not a finding.
  • Pages behind a login can only be audited self-hosted or pasted as HTML.
  • The demo console audits the sample shop and pasted HTML; live URLs need an API key and a verified domain.

Eval results, nightly checks and cost per runVerify a run

How it's builtThe steps, the models and what each one checks
Hosted · by Decosa

Get an API key

  • Call the storefront accessibility pass API from your own code in minutes.
  • Every model answer carries a signed receipt.
  • Nothing to install; we run the models.
Self-host · your GPUs

Run it yourself, on request

  • The same open models and app, on 1x RTX PRO 6000 (96 GB) for Qwen3.8-27B with its vision tower; headless Chromium and axe-core on CPU.
  • Data never leaves your machines, and there are no Decosa charges.
  • One prompt for Claude Code or Codex assembles the whole stack.
  • Early access: the container images are not public yet and the source needs access; the prompt says how to ask.
The open stack

Audit up to five pages of an online shop: automated WCAG 2.2 rules, a keyboard pass over add-to-cart and checkout, open-model judgements on alt text, names and error messages, and a fix list ranked by what blocks a purchase.

A private headless Chromium opens the shop's pages and runs axe-core's automated WCAG 2.2 A and AA rules. It then tabs through the page to check that add-to-cart and the checkout can be reached and used with the keyboard, that focus is visible and that no dialog traps focus, and submits empty forms to read the error messages (it never sends a real order). Qwen3.8-27B with vision answers what a rule engine cannot: is this alt text right for this image, is a link or button name clear in context, does an error message say how to fix the field, is an offer only inside an image. Each finding comes with its WCAG success criterion, the element and a code fix, and a signed record says what was checked. For small online shops selling into the EU or the US, and the agencies that build them.

Deployment
Hosted or self-host
Regulatory
An audit aid, not a certification or a statement that a site conforms to WCAG, the ADA or the European Accessibility Act; not legal advice. Sources read on the primary pages on 27 Sep 2026. EU: the European Accessibility Act, Directive (EU) 2019/882 (https://eur-lex.europa.eu/eli/dir/2019/882/oj/eng), covers e-commerce services to consumers (Art. 2(2)(f), defined in Art. 3(30)) and applies from 28 Jun 2025 (Art. 31(2)); microenterprises providing services (fewer than 10 staff and annual turnover or balance sheet of no more than EUR 2 million, Art. 3(23)) are exempt (Art. 4(5)); harmonised standards give a presumption of conformity (Art. 15), and the one used for web content, EN 301 549, points to WCAG 2.1 AA (the EN 301 549 version was not re-checked); penalties are set by each Member State (Art. 30). US: DOJ's web guidance of 18 Mar 2022 (https://www.ada.gov/resources/web-guidance/) says ADA Title III covers the goods and services businesses open to the public offer online, with no Title III regulation setting a technical standard; WCAG is named as helpful guidance, and lawsuits and settlements usually cite WCAG 2.1 AA. The Title II web rule (28 CFR part 35 subpart H, published 24 Apr 2024, https://www.ada.gov/resources/2024-03-08-web-rule/) sets WCAG 2.1 AA for state and local governments only, not private shops; an interim final rule published 20 Apr 2026 moved its compliance dates to 26 Apr 2027 and 26 Apr 2028. Overlays: the FTC's final order against accessiBe (22 Apr 2025, USD 1 million, https://www.ftc.gov/news-events/news/press-releases/2025/04/ftc-approves-final-order-requiring-accessibe-pay-1-million) bars claims that an automated tool makes a site WCAG-compliant without evidence, which is why this pass reports what it checked and never says compliant. Licences: Qwen3.8-27B Apache-2.0; axe-core MPL-2.0 (vendored unmodified, source and licence shipped with it); Playwright Apache-2.0; Chromium BSD-3-Clause.
Architecture
Text description

Up to five shop pages (the made-up sample shop, pasted HTML, or URLs on a domain the API key has verified) open in a private headless Chromium inside decosa-api, which can run on your own machine. axe-core 4.13 (MPL-2.0) runs the automated WCAG 2.2 A and AA rules; a keyboard pass tabs to add-to-cart and the checkout, presses Enter, checks visible focus and escape from dialogs; a form pass submits empty forms and reads the errors, with every non-GET request blocked. Qwen3.8-27B with vision (Apache-2.0) judges each image against its alt text, link and button names in context, the error messages, and text that exists only inside an image. Findings are ranked P1 to P4 with the WCAG success criterion, the element and a code fix, and sealed in a signed record. It never states that a site conforms. Hosted model calls get gateway receipts, countersigned.

Architecture

At a glance

What it checks
axe-core's automated WCAG 2.2 A/AA rules; add-to-cart and the checkout with the keyboard alone (reachable, answers Enter or Space, visible focus, no trap); error messages after an empty submit; and, by the model, whether each alt text fits its image, whether link and button names are clear in context, whether errors say how to fix the field, and offer text that exists only inside an image. Up to five pages of one shop per audit, desktop or 390 px wide.
What it does not do
It does not certify conformance or say a site meets WCAG, the ADA or the European Accessibility Act, and it is not legal advice. It does not test screen-reader output, reading order, zoom and reflow, captions or cognitive load, every widget, or pages behind a login on the hosted service. It never pays, places an order or submits a form to your server. The model judgements are suggestions for a person to review.
Data retention
Nothing is stored: pages, screenshots and images live in memory and a private browser profile for the audit and are dropped when it ends. The result and its signed record are returned to you, not kept. Logs carry counts and ids, never URLs, alt text or page text.
What leaves the box (hosted demo)
Page images, alt text, link and button names and error messages go to Qwen3.8-27B through our gateway, operated by Decosa. The headless browser only fetches the pages you list, on a domain your key has verified. Self-hosted, nothing leaves the machine except the browser's requests to your own site.
Output
A fix list ranked P1 (blocks a purchase) to P4, each finding with its WCAG 2.2 success criteria, the page, the element's HTML and a code fix, plus advisories, what was and was not checked, and a signed record (verify at /record/verify) with the axe-core version and hash, the prompts' hashes and a receipt per model call.
Cost per page
A fraction of a cent of model calls per page (a few calls) at the gateway list price, measured over the test pages. Without the model it is CPU only.
Quality tiers

Pick the tier for the quality you need

Same app at every tier. What changes is the models, the hardware they need, and whether receipts are signed. Scores are measured with the source named, or marked not measured.

  • In the hosted demo

    Lite

    rules, keyboard and forms, no model (CPU)

    axe-core, the keyboard pass and the form pass only ("judge": false): no GPU, no model calls. Misses wrong or keyword-stuffed alt text, vague names, unclear error messages and text only in images.

    Models
    • axe-core 4.13.0 in headless Chromium (Playwright 1.58)
    • decosa-api a11y module (decosa_api/verticals/a11y)
    Hardware
    CPU only (a headless Chromium per audit)
    Quality evidence
    • held-out test: planted issues found by the rule engine and the keyboard and form passes (no model)70 of 70 of those types; the 57 model-layer issues are not checkeddecosa-api docs/evals/storefront-accessibility-pass.md, measured on our server 2026-09-27, gateway route
    Latency
    measured: seconds for the sample shop without the model (console run)
    Verification
    Proof: partialNo model calls; the record is signed by the instance (attested).
  • In the hosted demo

    Standard

    rules, keyboard, forms and the model (hosted demo)

    Adds Qwen3.8-27B with vision for the judgements a rule engine cannot make: alt text against the image, names in context, error messages, text only in images.

    Models
    • Qwen3.8-27B (NVIDIA NVFP4)
    • axe-core 4.13.0 in headless Chromium (Playwright 1.58)
    • decosa-api a11y module (decosa_api/verticals/a11y)
    Hardware
    Qwen3.8-27B on one 96 GB card, or the hosted gateway; the rest on CPU
    Quality evidence
    • held-out test (frozen): planted issues caught / clean items flagged / clean pages with any finding122 of 127 / 0 of 207 / 0 of 7decosa-api docs/evals/storefront-accessibility-pass.md, measured on our server 2026-09-27, gateway route
    • blind comparison on 60 alt and 40 name cases: Qwen3.8-27B vs Claude Code Opus 5.5alt 57/60 vs 59/60; planted names 10/12 vs 11/12decosa-api docs/evals/storefront-accessibility-pass.md, measured on our server 2026-09-27, gateway route
    Latency
    measured: under a minute per page under eval load on the shared gateway
    Verification
    Proof: strongGateway receipt per model call on the hosted route; the rule and keyboard findings are attested by the instance.
Components

Every model in the stack

Models in this stack. Each row has a button that shows its licence, engine, verification and evidence.
ModelDetails
Looks at each image with its alt text (right, wrong, poor, needed but empty, decorative), reads offer text drawn into banners, judges link and button names in their context and the form's error messagesQwen3.8-27B (NVIDIA NVFP4)nvidia/Qwen3.8-27B-NVFP4 on Hugging Face (opens in a new tab)
27.8B · 57 GBProof: strongIn the hosted demo
Opens each page in a private headless Chromium (one per audit), runs axe-core, the keyboard pass (Tab order, Enter on add-to-cart, visible focus, Escape from dialogs) and the empty-submit form pass; blocks every request that is not GET or HEADaxe-core 4.13.0 in headless Chromium (Playwright 1.58)
0 GBProof: partialIn the hosted demo
Merges the rule, keyboard, form and model results into findings ranked P1 (blocks a purchase) to P4, cites the WCAG 2.2 success criteria, writes a code fix per finding and seals the signed recorddecosa-api a11y module (decosa_api/verticals/a11y)
0 GBProof: partialIn the hosted demo

Around the models

Tools, services and hardware

Tools

Services

  • decosa-api:8445
    ${DECOSA_REGISTRY}/decosa-api:0.1.0

    GET /a11y/info, /a11y/samples, /a11y/store/*; POST /a11y/audit (SSE or JSON); /a11y/domains. Build with WITH_BROWSER=1 for the headless Chromium. No GPU.

  • decosa-llm:8000
    ${DECOSA_REGISTRY}/decosa-llm:0.1.0

    vLLM OpenAI endpoint for Qwen3.8-27B, served with its vision tower (image input). Internal to the compose network.

Hardware

  • 1x RTX PRO 6000 96 GB Fits

    The hosted setup: Qwen3.8-27B NVFP4 with its vision tower on one card; Chromium, axe-core and the rest on CPU.

  • CPU only Fits

    The rules, keyboard and form passes, the fix list and the record run on CPU ("judge": false); the alt-text, name and error-message judgements then need the hosted gateway or a GPU.

Latency per lane

  • one page, full audit (about 4 model calls), hosted gateway route51.8 s

    Measuredmeasured on our server 2026-09-27: median over the 30 test pages, with the alt eval running in parallel on a shared gateway (28.7 s median on the 20 dev pages)

  • the three-page sample shop, self-hosted (direct route to a local Qwen3.8-27B)11.6 s

    Measuredmeasured on our server 2026-09-27: 2 runs in the self-host check (10.7 and 12.6 s)

Assemble it

Run this exact stack on your machine

Paste into Claude Code / Codex to assemble this stack locally. The prompt checks your GPU, pulls the pinned models, writes the compose file and runs a smoke test.

storefront-accessibility-pass/assemble-prompt.md182 lines
# Assemble the Decosa storefront accessibility pass on this machine

You are setting up a self-hosted accessibility pass for an online shop on this Linux machine, for the shop's team or
the agency that builds it. For up to five pages of a shop it:
- runs axe-core's automated WCAG 2.2 A and AA rules in a private headless Chromium;
- tries add-to-cart and the checkout with the keyboard alone (reachable, answers Enter or Space, focus visible, no trap)
  and submits empty forms to read their error messages (never a real order: non-GET requests are blocked);
- asks Qwen3.8-27B with vision what a rule engine cannot: is the alt text right for this image, are link and button
  names clear in context, does each error message say how to fix the field, is an offer only inside an image;
- returns a fix list ranked by what blocks a purchase, each finding with its WCAG success criterion and a code fix, and a
  signed record of what was checked.

Work step by step. Show me each command before running anything that needs sudo, and stop if a check fails.

**Before anything else, remind me:**
- This is an audit aid, not a certification and not a statement that the site conforms to WCAG, the ADA or the European
  Accessibility Act. Automated rules and model judgements miss things; a person reviews every finding. Not legal advice.
- Staging sites and pages behind a login stay on this machine: the model route stays local (`direct`).
- Only audit sites I own or have permission to test.

Repeat these points in your final summary.

## Step 0: set up with a coding agent, rehearse on mock data, then go private

This prompt is for a coding agent running on the machine that will host the service. We recommend Claude Code with
Claude Opus 5.5; any capable coding agent works. Work in this order:

1. Set up on mock data only. During the whole setup you (the agent) work with the synthetic sample bundle below and
   nothing else. Do not ask me for real data, and do not open, read, list or copy files that hold real data, even to
   "test with something realistic".
2. Rehearse. When the steps below are done and the service is healthy, fetch the mock-data bundle for this tool,
   https://decosa.ai/samples/storefront-accessibility-pass.zip (58 KB, 8 checks, synthetic or openly licensed: see `licence` in expected.json),
   show me what is in it, and run the rehearsal against the local API:
   `docker compose exec api python scripts/rehearse.py storefront-accessibility-pass` (the api image carries the same bundle under /app/rehearsal/storefront-accessibility-pass/;
   with no key set, the script asks the local API for a short demo token). From a decosa-api checkout instead:
   `python scripts/rehearse.py storefront-accessibility-pass --bundle storefront-accessibility-pass.zip --base-url http://127.0.0.1:<PORT>`.
   It sends the mock inputs to the local API and prints PASS or FAIL for each expected property (for example: "Add to cart, a div the keyboard cannot reach, is found and ranked first (P1)", "the quantity field without a label is found by the rule engine", "the packshot's alt text, which calls the orange can purple, is flagged by the model"). Show me
   the full output. Every check must pass. If one fails, fix the install and run it again; never edit `expected.json`
   to make a check pass.
3. Stop there. Once the rehearsal passes, tell me, and I will run my own data against the local API myself, on this
   machine.

For the person running this: a coding agent that runs in the cloud sees everything in its context, including files it
reads, command output and anything pasted into the chat. Keep real data out of the chat and out of anything the agent
can read. Switch to your own data only after the rehearsal has passed and the agent's work is done.

## What you are building

| service | image | model | port |
|---|---|---|---|
| `llm` | `${DECOSA_REGISTRY}/decosa-llm:0.1.0` (vLLM 0.29.0, `vllm/vllm-openai@sha256:c2914767605584b6d8f45686b82de173ecc99e781897aa3d0a66dacd72c51ae1`) | `nvidia/Qwen3.8-27B-NVFP4` @ `482ca0f3832238542f8f5295dde86b5f22711d80`, Apache-2.0, **with its vision tower** | internal 8000 |
| `api` | `${DECOSA_REGISTRY}/decosa-api:0.1.0` built **with `WITH_BROWSER=1`** (headless Chromium, BSD-3-Clause; Playwright, Apache-2.0; axe-core 4.13.0, MPL-2.0, vendored in the package) | none | `127.0.0.1:8445` |

The browser, axe-core, the keyboard and form passes, the fix list and the record run on CPU. Without the model you can
still run the rules, keyboard and form passes (`"judge": false`).

## 1. Check the GPU, driver and Docker

1. Run `nvidia-smi`. You need one NVIDIA GPU with at least 64 GB (the measured setup is an RTX PRO 6000 96 GB) and driver
   580 or newer.
2. Check `docker --version`, `docker compose version` and `docker run --rm --gpus all ubuntu nvidia-smi`. If Docker or the
   NVIDIA Container Toolkit is missing, install them from the official repositories
   (`sudo nvidia-ctk runtime configure --runtime=docker`, then restart Docker).
3. Confirm about 70 GB of free disk.

## 2. Get the images

The images are **on request** while self-host is in early access: ask at https://decosa.ai/contact?topic=self-host, and Decosa sends the registry (set it as `DECOSA_REGISTRY`), pull access and the compose file.
1. Try `docker pull ${DECOSA_REGISTRY}/decosa-llm:0.1.0`.
2. Build the api image with the browser from the `decosa-api` source (once published):
   `docker build -f docker/api/Dockerfile --build-arg WITH_BROWSER=1 -t ${DECOSA_REGISTRY}/decosa-api:0.1.0 .`
   (`WITH_BROWSER=1` is required: without it `GET /a11y/info` reports `browser_available: false` and audits return 503).
3. If none of this works, stop and tell me.

## 3. Write the compose file

Create `~/decosa-a11y/.env`:

```bash
DECOSA_TAG=0.1.0
DECOSA_GPU=0
LLM_MODEL=nvidia/Qwen3.8-27B-NVFP4
LLM_REVISION=482ca0f3832238542f8f5295dde86b5f22711d80
LLM_MAX_LEN=65536
LLM_GPU_UTIL=0.80
DECOSA_SIGNER_NAME="<who signs these records, e.g. Example Shop web team>"
DECOSA_ADMIN_SECRET=<a long random string; keep this file mode 0600>
# hosts this box may audit without DNS verification, e.g. staging.example-shop.com,localhost
AUDIT_HOSTS=
```

Create `~/decosa-a11y/docker-compose.yml`:

```yaml
name: decosa-a11y
x-health: &health
  interval: 15s
  timeout: 5s
  retries: 5
services:
  llm:
    image: ${DECOSA_REGISTRY}/decosa-llm:${DECOSA_TAG}
    deploy: { resources: { reservations: { devices: [ { driver: nvidia, device_ids: ["${DECOSA_GPU:-0}"], capabilities: [gpu] } ] } } }
    ipc: host
    restart: unless-stopped
    volumes: [hf-cache:/root/.cache/huggingface]
    # no --language-model-only: the pass sends one image per alt-text call
    command: ["${LLM_MODEL}", "--revision", "${LLM_REVISION}", "--served-model-name", "qwen3.8-27b",
              "--max-model-len", "${LLM_MAX_LEN}", "--gpu-memory-utilization", "${LLM_GPU_UTIL}", "--max-num-seqs", "16",
              "--kv-cache-dtype", "fp8_e4m3", "--speculative-config", '{"method":"mtp","num_speculative_tokens":3}',
              "--limit-mm-per-prompt", '{"image":4,"video":0}', "--seed", "0", "--enable-force-include-usage", "--host", "0.0.0.0", "--port", "8000"]
    healthcheck: { <<: *health, test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=4)"], start_period: 900s }
  api:
    image: ${DECOSA_REGISTRY}/decosa-api:${DECOSA_TAG}
    restart: unless-stopped
    depends_on: { llm: { condition: service_healthy } }
    shm_size: 1gb                               # Chromium needs more than Docker's 64 MB default
    environment:
      DECOSA_LLM_ROUTE: direct                  # local model only; receipts are signed by this box's key ("attested")
      DECOSA_LLM_URL: http://llm:8000/v1
      DECOSA_LLM_MODEL: qwen3.8-27b
      DECOSA_LOCAL_SIGNING: "on"
      DECOSA_SIGNER_NAME: ${DECOSA_SIGNER_NAME}
      DECOSA_ADMIN_SECRET: ${DECOSA_ADMIN_SECRET}
      DECOSA_TESTRUNS_TARGETS: ${AUDIT_HOSTS}   # shared with test runs: hosts audited without domain verification
      DECOSA_TESTRUNS_ALLOW_PRIVATE: "1"        # lets those hosts resolve to private addresses (staging on your LAN)
      DECOSA_A11Y_MAX_CONCURRENT: "2"
      DECOSA_SESSIONS_PER_IP_HOUR: "1000"
      DECOSA_BUDGET_LLM_TOKENS: "200000"
      DECOSA_CORS_ORIGIN_REGEX: '^https?://(localhost|127\.0\.0\.1)(:\d+)?$$'
    ports: ["127.0.0.1:8445:8445"]
    volumes: [decosa-data:/data]
    healthcheck: { <<: *health, test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8445/healthz', timeout=4)"], start_period: 20s }
volumes: { hf-cache: {}, decosa-data: {} }
```

Use the named volumes as written: a host bind mount owned by root makes the API fail on `/data/keys.sqlite` (the api image
owns `/data`).

## 4. Start it

1. `docker compose up -d`, then poll `docker compose ps` until both are healthy (the LLM takes 5-10 minutes the first time).
2. `curl -s localhost:8445/a11y/info | jq '{browser_available, axe_core, route: .models.route}'`: `browser_available: true`,
   axe-core `4.13.0` with licence `MPL-2.0`, route `direct`.

## 5. Smoke test

```bash
cd ~/decosa-a11y && docker compose exec api python scripts/rehearse.py storefront-accessibility-pass --base-url http://127.0.0.1:8445
```

Then run the two sample shops by hand:

```bash
API=localhost:8445
TOKEN=$(curl -s $API/demo/session -H 'content-type: application/json' -d '{"vertical":"storefront-accessibility-pass"}' | jq -r .token)
for s in shop-with-issues clean-shop; do
  curl -s $API/a11y/audit -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
    -d "{\"sample_id\":\"$s\",\"stream\":false}" | jq -c '{s: .summary, calls: .model_calls}'
done
```

Pass if `shop-with-issues` has P1 findings (Add to cart is a `<div>` the keyboard never reaches; the newsletter dialog
traps focus) plus alt-text, name and error-message findings from the model; `clean-shop` has no findings; the record in
each result verifies at `POST /record/verify`; every model receipt is `attested`.

## 6. Audit your own shop

- Mint a key: `KEY=$(curl -s -XPOST $API/v1/keys -H "authorization: Bearer $DECOSA_ADMIN_SECRET" -H 'content-type: application/json' -d '{"label":"a11y","verticals":["storefront-accessibility-pass"]}' | jq -r .key)`.
- Put the host in `AUDIT_HOSTS` (step 3; `http://` is allowed for a listed host) and restart the api. Then:
  `curl -s $API/a11y/audit -H "authorization: Bearer $KEY" -H 'content-type: application/json' -d '{"urls":["https://staging.example-shop.com/","https://staging.example-shop.com/products/one","https://staging.example-shop.com/cart"],"stream":false}'`
  Up to five pages on one origin. Add `"mobile": true` for a 390 px viewport.
- Without `AUDIT_HOSTS`, prove the domain first: `POST /a11y/domains {domain}` returns a token to publish as a DNS TXT
  record at `_decosa-challenge.<domain>` or at `/.well-known/decosa-verify.txt`, then `POST /a11y/domains/check`.
- A page you can't reach from this box can be pasted: `{"html": "...", "synthetic": true}` (the browser has no network
  then, so inline the images as `data:` URLs if you want them judged).

## 7. Point your tools at it

- The console on the Decosa site talks to `NEXT_PUBLIC_DECOSA_API`; set it to `http://127.0.0.1:8445` for a local build.
- In CI, run the pass on every theme release and fail the build on new P1 findings; keep each signed record with the
  release.
Rules and regulations it checks againstDated, linked to the primary source; not legal advice

Regulation watch

Loading the watch status…

3 laws, rules and guidance pages cited; 2 watched nightly at the primary source. A change marks this page for a human re-check; nothing is edited automatically. What we cite and how it is watched

Technical detailsModels, where it runs, labels

In short

Last reviewed

What it is
A storefront accessibility audit for up to five pages of a shop: axe-core's WCAG 2.2 rules, a keyboard-only pass to add-to-cart and checkout, and an open model that checks alt text against each image, link names and error messages. You get a ranked fix list and a signed record.
Who it's for
Small online shops selling into the EU or the US, and the agencies and theme developers who build them.
Where it runs
Hosted or self-host
Key numbers

On the held-out test pages of a made-up shop it caught 122 of 127 planted issues and flagged none of 207 clean items; one synthetic shop template, no real themes tested.

  • 122 / 127 Planted issues caught (test split, n = 127)
  • 0 / 207 Clean items flagged (test split, n = 207)
  • 0 / 7 Clean pages with any finding (test split, n = 7)
  • 1.8 s Median end-to-end run, hosted (QA sweep 2026-09-28)
All results, datasets and caveats
Models
Qwen3.8-27B with vision (alt text against the image, text in images, link and button names, error messages) · axe-core 4.13 (MPL-2.0, automated WCAG rules) in headless Chromium
Where
Hosted or self-host
Checks
Receipt per model call; signed hash-chained record with the axe-core version and hash, the prompts' hashes, the pages and every finding
Output
Signed record or verdict · Structured data
Data
No sensitive data
Hardware
1× 96 GB GPU
Licence
Permissive (Apache-2.0, MIT)
Built from
Signed record

Questions people ask

What does a storefront accessibility audit here check?

axe-core's automated WCAG 2.2 A and AA rules, add-to-cart and the checkout with the keyboard alone (reachable, answers Enter, visible focus, no trap), error messages after an empty submit, and, by Qwen3.8-27B with vision, whether each alt text fits its image, whether link and button names are clear, whether errors say how to fix the field, and offer text that exists only inside an image.

Will it tell me my shop is compliant?

No. It is an audit aid, not a certification or legal advice. It reports what it checked and what it found, and the signed record says a person still has to review it. The FTC's April 2025 order against accessiBe bars unsupported claims that an automated tool alone makes a site meet WCAG.

Does the European Accessibility Act apply to my shop?

The Act (Directive 2019/882) covers e-commerce services to EU consumers from 28 June 2025. Microenterprises providing services, with fewer than 10 staff and no more than EUR 2 million turnover or balance sheet, are exempt. Enforcement is set by each Member State. This is a summary of the text, not legal advice.

How accurate is it?

On held-out test pages of a made-up shop it caught 122 of 127 planted issues across 17 types and flagged none of 207 clean items. Judging alt text alone, it caught 111 of 113 bad alts and flagged none of 38 good ones. It has not been measured on real shop themes.

Can it audit my live shop?

With an API key and a domain you verify by DNS TXT record or a well-known file, for up to five pages at a time; the browser sends only GET and HEAD requests, so it never places an order. Self-hosted, it can audit a staging site on your own network.

Ask a question or leave feedbackWe read every message and publish useful answers
Questions & feedback

Ask about Storefront accessibility pass

We read every message. Questions, comments and our answers show here once we have reviewed and approved them.

Loading questions…

This is a

Plain text. Please leave out personal, patient or client data.

Shown with your message if we publish it. Leave blank to post as “A visitor”.

Nothing appears here until we have read and approved it.