Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ef674b4
Make AccessForge authentication and service defaults portable
mycomind4-arch Jul 16, 2026
e5c6a51
Use configured login domains in every environment
mycomind4-arch Jul 16, 2026
b992f31
Require an explicit URL audit allowlist
mycomind4-arch Jul 16, 2026
bd3e739
Repair and apply portable AccessForge configuration literally
mycomind4-arch Jul 16, 2026
600476c
Test explicit and fail-closed URL allowlists
mycomind4-arch Jul 16, 2026
b415d50
Use deployment-neutral URL policy fixtures
mycomind4-arch Jul 16, 2026
b797abe
Test portable fail-closed email domain configuration
mycomind4-arch Jul 16, 2026
e0be66d
Make scoring descriptions deployment-neutral
mycomind4-arch Jul 16, 2026
c1651c9
Make OTP email branding portable
mycomind4-arch Jul 16, 2026
afbccfe
Fail clearly when publication endpoint is unconfigured
mycomind4-arch Jul 16, 2026
f015be6
Rename CLI package for AccessForge
mycomind4-arch Jul 16, 2026
ac8e4a2
Run renamed AccessForge CLI test suite
mycomind4-arch Jul 16, 2026
ed34fb3
Document portable production configuration
mycomind4-arch Jul 16, 2026
8fde693
Document portable local configuration
mycomind4-arch Jul 16, 2026
152c00b
Use AccessForge production app name
mycomind4-arch Jul 16, 2026
898029b
Document complete AccessForge deployment configuration
mycomind4-arch Jul 16, 2026
09a39b6
Remove Illinois-specific public metadata
mycomind4-arch Jul 16, 2026
6676cad
Forward portable AccessForge configuration to container
mycomind4-arch Jul 16, 2026
a2b5c7d
Use AccessForge process names and forward configuration
mycomind4-arch Jul 16, 2026
28975ba
Document AccessForge portable deployment defaults
mycomind4-arch Jul 16, 2026
59c21d5
Make machine-readable product description deployment-neutral
mycomind4-arch Jul 16, 2026
3eb7bbb
Make standards and retention copy organization-neutral
mycomind4-arch Jul 16, 2026
39e2a18
Make full machine-readable docs deployment-neutral
mycomind4-arch Jul 16, 2026
6d0015b
Make technical details deployment-neutral
mycomind4-arch Jul 16, 2026
8f76be0
Advance AccessForge near-term roadmap
mycomind4-arch Jul 16, 2026
3902f6b
Document environment-driven login configuration
mycomind4-arch Jul 16, 2026
ecd9f5a
Require a verified sender for production authentication
mycomind4-arch Jul 16, 2026
818d4f3
Test required production SMTP sender
mycomind4-arch Jul 16, 2026
d1816af
Remove unused auth test import
mycomind4-arch Jul 16, 2026
046044a
Make publication endpoint injectable for testing
mycomind4-arch Jul 16, 2026
65b9744
Cover configured and missing publication endpoints
mycomind4-arch Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
# Public HTTPS origin in production. It must exactly match the browser origin.
PRODUCTION_URL=http://localhost:5102

# Authentication is disabled by default. To enable it, set true and configure
# allowed email domains, a JWT secret, and SMTP below.
AUTH_REQUIRE_LOGIN=false
ALLOWED_DOMAINS=example.org
JWT_SECRET=
ADMIN_EMAILS=admin@example.org

EMAIL_PROVIDER=mailgun
SMTP_HOST=smtp.mailgun.org
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
SMTP_FROM=AccessForge <no-reply@example.org>

# Hostnames that URL-audit endpoints may fetch, comma-separated.
# Use customer-approved public hosts; private/reserved IPs remain blocked.
# Empty disables URL audits. Private/reserved IPs remain blocked.
ANALYZE_URL_ALLOWED_HOSTS=example.org,www.example.org

# Optional GraphQL endpoint used only by the CLI publist command.
PUBLIST_GRAPHQL_ENDPOINT=

# Optional link to an organization-specific accessibility standard.
IITAA_URL=

# Optional higher rate-limit tier for trusted automation.
# Generate with: openssl rand -hex 32
API_PRIVILEGED_TOKEN=

# Optional host port overrides.
ACCESSFORGE_WEB_PORT=5102
ACCESSFORGE_API_PORT=5103

2 changes: 1 addition & 1 deletion PRODUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Monthly monitoring is successful when the customer can identify new, fixed, regr

## Near-term roadmap

1. Remove the remaining upstream ICJIA-specific defaults and make authentication/allowed domains environment-configurable.
1. Validate the baseline-audit offer with three paying customers and record delivery time, false-positive rate, and repeat-purchase intent.
2. Add a monitored-target model with dated baselines and explicit scan-to-scan diffs.
3. Add organization boundaries, roles, export/delete controls, and tested backup/restore.
4. Add managed scheduling and notifications.
Expand Down
45 changes: 20 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AccessForge Document Compliance

[![Version](https://img.shields.io/badge/version-1.34.0-blue)](https://github.com/ICJIA/file-accessibility-audit/releases) [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) ![Tests](https://img.shields.io/badge/tests-1410%20passing-brightgreen) ![Node](https://img.shields.io/badge/node-%E2%89%A522-339933?logo=node.js&logoColor=white) ![Nuxt 4](https://img.shields.io/badge/Nuxt-4-00DC82?logo=nuxt&logoColor=white) ![TypeScript](https://img.shields.io/badge/TypeScript-5-3178C6?logo=typescript&logoColor=white) ![Audits: WCAG 2.2 AA](https://img.shields.io/badge/audits-WCAG%202.2%20AA-blueviolet)
[![Version](https://img.shields.io/badge/version-1.34.0-blue)](https://github.com/mycomind4-arch/AccessForge/releases) [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) ![Tests](https://img.shields.io/badge/tests-1410%20passing-brightgreen) ![Node](https://img.shields.io/badge/node-%E2%89%A522-339933?logo=node.js&logoColor=white) ![Nuxt 4](https://img.shields.io/badge/Nuxt-4-00DC82?logo=nuxt&logoColor=white) ![TypeScript](https://img.shields.io/badge/TypeScript-5-3178C6?logo=typescript&logoColor=white) ![Audits: WCAG 2.2 AA](https://img.shields.io/badge/audits-WCAG%202.2%20AA-blueviolet)

![AccessForge Document Compliance](apps/web/public/og-image.png)

Expand Down Expand Up @@ -30,28 +30,28 @@ being developed on top of that foundation.
> legal compliance. Human review and assistive-technology testing remain part
> of a defensible accessibility program.

A web tool that **audits** PDF, Word (.docx), PowerPoint (.pptx), and Excel (.xlsx) accessibility — and **(optionally) auto-remediates** PDFs — against [WCAG 2.2 AA](https://www.w3.org/WAI/WCAG22/quickref/) (a strict superset of [WCAG 2.1 AA](https://www.w3.org/WAI/WCAG21/quickref/), the legal minimum under [IITAA 2.1 §E205.4](https://doit.illinois.gov/initiatives/accessibility.html) and [ADA Title II](https://www.ada.gov/resources/title-ii-rule/)), and [Illinois IITAA 2.1](https://doit.illinois.gov/initiatives/accessibility.html) — all on infrastructure you control, with no AI and no per-document fees. To revert to WCAG 2.1 labels: set `WCAG_VERSION=2.1` and redeploy (API reverts on restart; web UI on rebuild).
A self-hosted web tool that **audits** PDF, Word (.docx), PowerPoint (.pptx), and Excel (.xlsx) accessibility — and **optionally auto-remediates** PDFs — using WCAG-aligned checks, detailed findings, and explicit manual-review indicators. Set `WCAG_VERSION=2.1` to use WCAG 2.1 labels; otherwise WCAG 2.2 is the default. Automated results are diagnostic and are not a legal compliance or certification claim.

## What it does

| | Feature | Detail |
|---|---------|--------|
| **9** | WCAG categories audited | Each document (PDF, Word, PowerPoint, or Excel) scored across the WCAG-aligned categories that apply to its format (up to 9) — a weighted 0–100 score (A–F grade) plus a separate, binary pass/fail **WCAG 2.2 conformance verdict**. |
| **F → A** | Auto-remediation (optional) | Tag untagged PDFs in seconds: qpdf → [OpenDataLoader](https://github.com/opendataloader-project/opendataloader-pdf) → [veraPDF](https://verapdf.org/). Output is rejected if it regresses the score. Manual review still recommended for IITAA compliance. |
| **PDF/UA-1** | Standards aligned | WCAG 2.2 AA (superset of 2.1 AA), ADA Title II (April 2026), Illinois IITAA 2.1, PDF/UA-1 via veraPDF. Full lifecycle audit trail with `fs.stat`-verified deletion events for compliance reporting. |
| **F → A** | Auto-remediation (optional) | Tag untagged PDFs in seconds: qpdf → [OpenDataLoader](https://github.com/opendataloader-project/opendataloader-pdf) → [veraPDF](https://verapdf.org/). Output is rejected if it regresses the score. Manual accessibility review is still required. |
| **PDF/UA-1** | Standards aligned | WCAG 2.2 AA-aligned checks (including the 2.1 criteria), ADA Title II context, and optional PDF/UA-1 signals via veraPDF. Full lifecycle audit trail with `fs.stat`-verified deletion events for compliance reporting. |
| **0** | Files retained | Audit: in-memory only, gone in seconds. Remediation: output deleted on first download or 30-minute TTL, then verified absent. |
| **$0** | No AI, no third-party APIs | Every step runs on your own server. No data sent to vision models, hosted AI services, or commercial PDF/Office SDKs. |
| **100%** | Open source | Apache 2.0 / MIT / MPL toolchain. No per-document fees, no SDK licensing. Designed for state agencies that need control over their pipeline. |
| **100%** | Open source | Apache 2.0 / MIT / MPL toolchain. No per-document fees, no SDK licensing. Designed for organizations that need control over their pipeline. |
| **3** | Files per batch | Upload up to 3 files (PDF, Word, PowerPoint, or Excel) at once; per-tab remediation for PDFs. `POST /api/analyze-url` for programmatic auditing of public documents. |
| **4** | Export formats | Text / HTML / Markdown / JSON report exports. 1-year shareable links (no login required to view). |

Auto-remediation is **disabled by default** — set `REMEDIATION_ENABLED=true` in your environment to enable. Architectural details in [docs/archive/pdf-remediation-integration-plan.md](docs/archive/pdf-remediation-integration-plan.md); the Phase 1 follow-on (interactive alt-text walkthrough) is specced in [docs/archive/pdf-remediation-alt-text-walkthrough-spec.md](docs/archive/pdf-remediation-alt-text-walkthrough-spec.md).

The intended workflow is: **upload → review findings → either auto-remediate or fix at the source (Word, InDesign, etc.) and re-export → re-upload to verify.** Manual review remains essential for full IITAA compliance regardless of which path is taken — the tool's job is to find issues and reduce the manual remediation surface, not replace human review.
The intended workflow is: **upload → review findings → either auto-remediate or fix at the source (Word, InDesign, etc.) and re-export → re-upload to verify.** Manual review remains essential regardless of which path is taken — the tool's job is to find issues and reduce the manual remediation surface, not replace human review.

## Contents

New here? The live tool is at **[audit.icjia.app](https://audit.icjia.app)**; this README is the technical companion. Jump to:
New here? Start with the local Docker deployment in [PRODUCT.md](PRODUCT.md); this README is the technical companion. Jump to:

- **Overview** — [What it does](#what-it-does) · [Scoring rubric](#scoring-rubric)
- **Run it** — [Quick Start](#quick-start) · [Authentication](#authentication) · [Configuration](#configuration) · [Deployment](#deployment)
Expand Down Expand Up @@ -119,26 +119,27 @@ pnpm rebrand # Regenerate static files after changing BRANDING in audit.confi

## Authentication

Authentication is **off by default**. The app can be used without any login, email provider, or credentials. This is controlled by a single toggle in `audit.config.ts`:
Authentication is **off by default**. Enable email-OTP login through environment configuration:

```ts
export const AUTH = {
REQUIRE_LOGIN: false, // ← set to true to enable OTP authentication
// ...
};
```env
AUTH_REQUIRE_LOGIN=true
ALLOWED_DOMAINS=example.org,partner.example
JWT_SECRET=replace-with-a-high-entropy-secret
```

### With auth disabled (`REQUIRE_LOGIN: false` — default)
When login is enabled in production, configure a verified SMTP sender and credentials as shown in `apps/api/.env.example.production`.

### With auth disabled (`AUTH_REQUIRE_LOGIN=false` — default)

- Users go straight to the upload page — no login screen
- No email provider or SMTP credentials needed
- No audit history is recorded (no user identity to associate with analyses)
- All security protections (rate limiting, file validation, CORS) remain active

### With auth enabled (`REQUIRE_LOGIN: true`)
### With auth enabled (`AUTH_REQUIRE_LOGIN=true`)

- Users must authenticate via a **6-digit one-time password (OTP)** sent to their email
- Only `illinois.gov` email addresses are accepted (configurable via `AUTH.ALLOWED_EMAIL_REGEX`)
- Only domains listed in `ALLOWED_DOMAINS` are accepted; exact domains and their subdomains are allowed
- Sessions last 72 hours via JWT in an httpOnly cookie — no passwords stored
- All analyses are logged with the authenticated user's email for audit history
- **Requires an email provider** — the app needs to send OTP codes (see below)
Expand All @@ -152,20 +153,14 @@ When authentication is enabled, the app sends one-time passcodes via email. This
| Mailgun (default) | [docs/archive/07-mailgun-integration.md](docs/archive/07-mailgun-integration.md) |
| SMTP2GO | [docs/archive/06-smtp2go-integration.md](docs/archive/06-smtp2go-integration.md) |

The provider is controlled in `audit.config.ts` → `EMAIL.PROVIDER`. Credentials go in `apps/api/.env`:
The provider and SMTP connection are controlled by environment variables. Credentials go in `apps/api/.env`:

```env
SMTP_USER=your-smtp-login
SMTP_PASS=your-smtp-password
```

**To switch providers**, change one line in `audit.config.ts`:

```ts
PROVIDER: "mailgun"; // ← change to 'smtp2go' to switch
```

Host and port are set automatically per provider.
**To switch providers**, set `EMAIL_PROVIDER=mailgun` or `EMAIL_PROVIDER=smtp2go`; override `SMTP_HOST` and `SMTP_PORT` for another compatible relay.

**Dev note:** When running locally with auth enabled, OTP codes are printed to the API console — no email credentials needed for development.

Expand Down Expand Up @@ -1296,7 +1291,7 @@ Security reviews for prior releases were not yet captured in this format. Going

## Changelog

See [CHANGELOG.md](CHANGELOG.md) for a full list of changes by version, or view [releases on GitHub](https://github.com/ICJIA/file-accessibility-audit/releases).
See [CHANGELOG.md](CHANGELOG.md) for a full list of changes by version, or view [releases on GitHub](https://github.com/mycomind4-arch/AccessForge/releases).

## License

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Important limitations:
- [ ] Set `ANALYZE_URL_ALLOWED_HOSTS` to the customer-approved public hosts; keep private/reserved-address blocking enabled.
- [ ] Generate high-entropy values for every enabled secret; do not reuse development values.
- [ ] If the privileged API tier is enabled, generate and rotate `API_PRIVILEGED_TOKEN` as a secret and never put it in URLs or logs.
- [ ] If login is enabled in code, configure a strong `JWT_SECRET`, verified SMTP sender, approved domains, and named administrators; test login, logout, revocation, and account removal.
- [ ] If `AUTH_REQUIRE_LOGIN=true`, configure a strong `JWT_SECRET`, verified `SMTP_FROM`, SMTP credentials, `ALLOWED_DOMAINS`, and named administrators; test login, logout, revocation, and account removal.
- [ ] Keep `REMEDIATION_ENABLED=false` unless the engagement explicitly requires it and its external runtime is tested.
- [ ] Back up the SQLite data volume, encrypt backups, test restore, and document retention/deletion ownership.
- [ ] Run `pnpm install --frozen-lockfile`, `pnpm lint`, `pnpm typecheck`, `pnpm build`, and `pnpm test` for the exact deployed commit.
Expand Down
28 changes: 16 additions & 12 deletions apps/api/.env.example.local
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
NODE_ENV=development
PORT=5103

# Generate with: openssl rand -hex 32
AUTH_REQUIRE_LOGIN=false
ALLOWED_DOMAINS=example.org
JWT_SECRET=dev-secret-do-not-use-in-production

# Database
DB_PATH=./data/audit.db

# Email credentials (provider is set in audit.config.ts → EMAIL.PROVIDER)
# In dev, OTP codes are logged to console — these are optional locally.
SMTP_USER=postmaster@icjia.cloud
SMTP_PASS=your-mailgun-smtp-password
# SMTP_FROM=admin@icjia.cloud # optional — defaults to EMAIL.DEFAULT_FROM in config
# SMTP_HOST= # optional — defaults to provider host in config
# SMTP_PORT= # optional — defaults to provider port in config
# Optional locally. Without SMTP credentials, OTP codes are logged to the console.
EMAIL_PROVIDER=mailgun
SMTP_HOST=smtp.mailgun.org
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
SMTP_FROM=AccessForge <no-reply@example.org>

# Limits
MAX_FILE_SIZE_MB=15
TMP_DIR=/tmp

# Admin
ADMIN_EMAILS=dev@test.illinois.gov
# Admin — comma-separated email addresses
ADMIN_EMAILS=admin@example.org

# Development: comma-separated extra domains for testing
ALLOWED_DOMAINS=illinois.gov
# URL audits are disabled until explicitly allowlisted.
ANALYZE_URL_ALLOWED_HOSTS=example.org,www.example.org

# Optional GraphQL endpoint used only by the CLI publist command.
PUBLIST_GRAPHQL_ENDPOINT=
22 changes: 16 additions & 6 deletions apps/api/.env.example.production
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
NODE_ENV=production
PORT=5103

# Authentication is off by default. When enabled, all settings below are required.
AUTH_REQUIRE_LOGIN=false
ALLOWED_DOMAINS=example.org
# Generate with: openssl rand -hex 32
JWT_SECRET=REPLACE-WITH-REAL-SECRET

# Database
DB_PATH=./data/audit.db

# Email credentials (provider is set in audit.config.ts → EMAIL.PROVIDER)
SMTP_USER=postmaster@icjia.cloud
# SMTP (required only when authentication is enabled)
EMAIL_PROVIDER=mailgun
SMTP_HOST=smtp.mailgun.org
SMTP_PORT=587
SMTP_USER=your-smtp-login
SMTP_PASS=REPLACE-WITH-REAL-PASSWORD
SMTP_FROM=AccessForge <no-reply@example.org>

# Limits
MAX_FILE_SIZE_MB=15
TMP_DIR=/tmp

# Admin — comma-separated list of admin email addresses
ADMIN_EMAILS=REPLACE-WITH-REAL-ADMIN-EMAILS
# Admin — comma-separated email addresses
ADMIN_EMAILS=admin@example.org

# Production: leave empty — only illinois.gov regex is enforced
ALLOWED_DOMAINS=
# URL audits are disabled until explicitly allowlisted.
ANALYZE_URL_ALLOWED_HOSTS=example.org,www.example.org

# Optional GraphQL endpoint used only by the CLI publist command.
PUBLIST_GRAPHQL_ENDPOINT=
Loading
Loading