Self-hosted accessibility scanner powered by axe-core. Scan sitemaps, capture screenshots, review issues in a dashboard.
Quick start | Deploy | Docs | Roadmap | Releases
LIME is a batteries-included accessibility scanner you host yourself. Point it at a sitemap or a URL, it walks the pages with a headless Chromium, runs the full axe-core ruleset (WCAG A/AA, Lighthouse-aligned), captures focused screenshots of each violation, and groups the results into reviewable issues with ACT rule guidance.
Built to be cheap to run on a small server, straightforward to push to Fly.io, and safe to leave alone. Migrations apply on boot, interrupted scans recover on restart, and updates are one command.
- Full-site scans driven by sitemap or sitemap-index, with viewport presets (desktop / laptop / tablet / mobile).
- Per-issue screenshots - highlighted visible capture + inline preview, spotlight overlay, focus/hover settle pass.
- Axe-core + ACT - Lighthouse-aligned rule set, ACT catalog bundled locally (no runtime W3C calls).
- Reports - PDF, CSV, and compact LLM-ready exports per scan or issue.
- False-positive triage flag per issue.
- Partial scan retry - retry failed pages inside the same scan without creating a second scan.
- Scan recovery - non-terminal scans resume after Shopkeeper restarts.
- Update notice - opt-in sidebar banner when a new GitHub release is available.
- Three deploy targets - Fly.io, Docker, and Linux systemd.
- Shopkeeper - Go backend. Chi router, pgx, golang-migrate. Owns scan lifecycle, API routes, migrations, screenshots, PDFs, and the internal scan pipeline.
- UI - NextJS App Router, shadcn/ui, Drizzle ORM. Reads Postgres directly for server-rendered pages, proxies
/api/...to Shopkeeper at runtime so the image stays generic. - Database - PostgreSQL 17 shared by both services.
Shopkeeper pipeline:
| Stage | What it does |
|---|---|
| Profiler | Expands sitemap and sitemap-index inputs into a validated, deduplicated URL set for the scan. |
| Juicer | Takes those URLs, drives Chromium workers, runs axe-core, captures screenshots, and records per-page audit outcomes. |
| Sweetner | Takes Juicer audit output and writes normalized issues, occurrences, audits, and review-required records. |
Full architecture docs: lime.heysuman.com/docs/architecture/shopkeeper.
git clone https://github.com/sumanbasuli/lime.git
cd lime
cp .env.example .env
make start-allOpen:
Stop: make stop-all. Reset volumes: make clean.
LIME publishes versioned Docker images to GHCR from the main-branch release workflow. Use the release tag you want to deploy, for example v1.0.2.
| Target | One-liner | Guide |
|---|---|---|
| Fly.io | ./scripts/fly-deploy.sh v1.0.2 |
Fly.io guide |
| Docker | docker compose -f docker-compose.release.yml up -d |
Docker guide |
| Linux systemd | make build && sudo ./scripts/vps-install.sh |
Linux guide |
Fly Launch is Fly.io's app workflow around fly launch, fly.toml, and fly deploy; see Fly Launch for the platform overview. LIME uses that model, but it is a two-app deployment (Shopkeeper + UI), so do not run a single root-level fly launch and expect the whole stack to be configured. Use the included helper instead: scripts/fly-deploy.sh creates both apps, provisions screenshot storage, wires private networking, and deploys the published GHCR images.
Before deploying, install flyctl and authenticate:
flyctl auth loginUse either an external PostgreSQL URL:
export DATABASE_URL='postgresql://...'
./scripts/fly-deploy.sh v1.0.2Or attach Fly Managed Postgres to both apps first; the helper will reuse existing DATABASE_URL Fly secrets. Details: Fly.io deployment docs.
Every deploy target ships an update command that backs up the database, pulls the new version, migrates, and rolling-restarts the services.
./scripts/fly-update.sh v1.0.2 # Fly.io
make update-release TAG=v1.0.2 # Docker (release bundle)
sudo ./scripts/vps-update.sh v1.0.2 # Linux systemdDetails and rollback: update docs.
Runtime configuration lives in .env (root) or deploy-specific env files.
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
yes | PostgreSQL connection string, shared by UI and Shopkeeper. |
SHOPKEEPER_URL |
yes | URL the UI proxies /api/... to. |
LIME_IMAGE_REGISTRY |
release only | GHCR namespace for published images, e.g. ghcr.io/sumanbasuli. |
LIME_IMAGE_TAG |
release only | GHCR image tag, e.g. v0.1.0. |
LIME_API_PORT / LIME_UI_PORT |
optional | Published ports (defaults 8080 / 3000). |
LIME_UPDATE_CHECK |
optional | true shows a sidebar card when a newer release is on GitHub. Off by default. |
LIME_GITHUB_REPO |
optional | Override the update-check repo (default sumanbasuli/lime). |
ACT_RULES_PATH |
optional | Override the bundled ACT catalog path. |
See the setup docs for the full table.
make start-dev # full Docker stack with UI hot reload
make start-db # only Postgres
make dev-shopkeeper # Go backend with Air hot reload
make dev-ui # NextJS with hot reload| Command | Description |
|---|---|
make start-all |
Production-style Docker stack built from source |
make start-dev |
Development Docker stack with NextJS hot reload |
make migrate-all |
Apply DB migrations |
make build |
Build Go + Next + release bundle into dist/ |
make build-docker |
Build versioned Docker images locally |
make docs |
Refresh the static docs site with isolated heysuman.com demo screenshots |
make docs-run |
Build and serve the static docs site locally |
make docs-dev |
Run the docs site with NextJS hot reload |
make backup-db |
Dump the bundled Postgres to dist/backups/ |
make update TAG=vX.Y.Z |
Rebuild from a tag and rolling-restart |
make clean |
Stop the stack and remove volumes |
- Backend: Go 1.25, Chi, pgx, golang-migrate, chromedp
- Frontend: NextJS 16 (App Router), shadcn/ui, Drizzle ORM, TailwindCSS v4
- Browser: Chromium (Debian bookworm base image)
- DB: PostgreSQL 17
Public docs are published at lime.heysuman.com. The repo-local Markdown in docs/ remains the source content and contributor fallback.
make docs refreshes the docs site locally. It uses a separate lime-docs Docker Compose project, separate Postgres and screenshot volumes, non-default ports, and fresh single-page demo scans for https://heysuman.com, https://www.fake-university.com/, and https://overlaysdontwork.com/ so it never screenshots current private/local scans.
Issues and pull requests welcome. Before opening a PR:
- Run
make build; it must succeed. - Keep migrations forward-only and mirror any schema change in
lime/src/db/schema.ts. - Document new features in
docs/first;docs/index.mdis the source of truth.
See CONTRIBUTING.md for development setup, checks, and PR expectations.
Maintainers publish a release by updating VERSION, adding matching notes in CHANGELOG.md, and merging to main. release-docker.yml now runs make build, publishes the GHCR images through scripts/publish-release-images.sh, and creates or updates the GitHub Release through scripts/publish-github-release.sh. The release tag must be new; bump VERSION for every release.
Use GitHub Issues for bugs and deployment questions. Include the deploy target, release tag, relevant logs, and whether the scan was sitemap or single-page.
Security reports should follow SECURITY.md.
LIME is released under the GNU General Public License v2.0. See the COPYING file for the full text.
