VPAT Parser β first-party frontend proxy (#1318)
The public parser API (POST /api/v1/vpat-parse on the vpat-parse worker)
requires Authorization: Bearer <key> + per-key rate limiting and returns the
{ data, error, meta } envelope. The acr.gevell.com frontend
(apps/vpat) reaches it through a server-side proxy so the key never ships
in browser JS.
Request path
browser (acr.gevell.com) ββ POST /api/vpat-parse (same-origin, no key) ββ apps/vpat/worker/index.ts (injects Authorization: Bearer <key>) ββ POST {VPAT_PARSE_UPSTREAM}/api/v1/vpat-parse (keyed route)The proxy lives in apps/vpat/worker/index.ts (proxyParse). It runs before
the coming-soon gate (run_worker_first = true), overrides any client-supplied
Authorization, and passes the upstream envelope + status through verbatim
(copying only X-LLM-*, X-RateLimit-Remaining, Retry-After).
The client (apps/vpat/src/lib/parsers.ts) posts to the same-origin
/api/vpat-parse and reads the envelope: success β data, failure β throws a
ParseApiError carrying error.code, the HTTP status and error.details
(NO_CRITERIA_FOUND β AcrNoCriteriaError).
URL-submit error codes (admin tickets)
apps/vpat/src/lib/url-parse-error.ts maps a failed βpaste a URLβ parse to
guidance or a filed error. The old single ERR_ACR_FETCH_PARSE (empty context)
is split into:
| Code | Stage | Meaning |
|---|---|---|
ERR_ACR_URL_FETCH (warning) | fetch | The vendorβs page couldnβt be fetched (SOURCE_FETCH_FAILED: non-2xx, timeout, DNS/TLS error). |
ERR_ACR_URL_PARSE | parse | Page fetched, but the LLM parse failed (UPSTREAM_ERROR, INTERNAL_ERROR, malformed response). |
ERR_ACR_URL_SERVICE | service | Our own parser was unreachable/refused (NETWORK_ERROR, RATE_LIMITED, UNAUTHORIZED, config). |
Each recordβs context includes urlHost, urlPath (query string and fragment
are dropped because they can hold signed-download tokens), stage,
apiErrorCode, httpStatus, sourceStatus and sourceContentType. User-input
codes (UNSUPPORTED_MEDIA_TYPE for PDF/Word links, SOURCE_AUTH_REQUIRED,
URL_NOT_ALLOWED, SOURCE_TOO_LARGE, NO_CRITERIA_FOUND) show a warning
with what to do next and are not filed.
Configuration
apps/vpat/wrangler.toml:
VPAT_PARSE_UPSTREAM(var) β the keyed API base. Defaulthttps://vpat-parse.theaccessible.org.VPAT_PARSE_API_KEY(secret, not in the repo) β the first-party key.
Issuing / rotating the key
# 1. Mint a key (no DB access needed β prints the secret + an INSERT):node workers/vpat-parse/scripts/mint-api-key.mjs --name "acr.gevell.com frontend"
# 2. Run the printed INSERT in the converter Supabase (vpat_api_keys table).
# 3. Store the secret on the frontend worker:cd apps/vpat && npx wrangler secret put VPAT_PARSE_API_KEYTo rotate: mint a new key, set the secret, redeploy apps/vpat, then delete the
old vpat_api_keys row.
Local dev
next dev (port 3004) has no worker, so /api/vpat-parse 404s. Either run the
worker (npx wrangler dev in apps/vpat, port 8790) or point the client at it
with NEXT_PUBLIC_VPAT_PARSE_PROXY_URL=http://localhost:8790.
Legacy route β removed (#1322)
The legacy unauthenticated POST / is gone. The vpat-parse worker now
serves only GET /health and the keyed POST /api/v1/vpat-parse; every other
path returns 404. The keyed route (reached via this proxy) is the sole parse
path.
Deploy order: deploy
apps/vpat(the proxy) and confirm zero traffic on the oldPOST /before deploying thevpat-parseworker with the route removed, so no in-flight first-party caller breaks.