This project is a static frontend with a bring-your-own API model. The recommended backend contract is the JSON request plus JSONL stream described in docs/INTEGRATION.md.
Use a same-origin API route when you can:
https://reports.example.org/index.html
https://reports.example.org/api/query
Then set the app API URL to:
/api/query
This avoids most CORS complexity and keeps authentication cookies on one origin.
For private deployments with sign-in or existing institutional credentials, follow docs/AUTH.md. The short version is: keep sign-in, tokens, library-system credentials, and authorization checks on the backend or reverse proxy; point the frontend at a same-origin route such as /api/query.
The current Sirsi CGI backend fails closed by default. Before installing a hardened backend release, configure a trusted Apache/gateway REMOTE_USER path and QUERY_API_ADMIN_USERS, then verify the 401/403/authorized response matrix in staging. Do not deploy first and enable authentication afterward.
Put the static site and API behind the same public origin. A reverse proxy can route:
/ -> static frontend
/api/query -> backend adapter
Example Nginx shape:
location / {
root /var/www/query-frontend;
try_files $uri $uri/ /index.html;
}
location /api/query {
proxy_pass http://127.0.0.1:8787/query-api;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}Use CORS when the static frontend and API are on different origins:
https://reports.example.org/index.html
https://api.example.org/query-api
Minimum headers:
Access-Control-Allow-Origin: https://reports.example.org
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Expose-Headers: X-Query-Id, X-Raw-ColumnsFor the Sirsi CGI backend, list each exact frontend origin in QUERY_API_ALLOWED_ORIGINS. It emits the requesting origin only after an exact match and never emits a wildcard. If the API uses cookies, also configure credentials and SameSite policy carefully.
For internal deployments, prefer a normal server-side session:
- User signs in to the API origin or shared app origin.
- The API stores credentials and data-system access server-side.
- The browser sends only a session cookie.
- The API validates the session before running actions.
Do not put data-system passwords, API keys, or long-lived tokens into the API Settings URL.
For OIDC, SAML/Shibboleth, CAS, LDAP-backed identity providers, or institutional SSO, terminate sign-in in the backend, reverse proxy, or identity-aware gateway. The browser app should only call the authenticated same-origin Query API route after sign-in.
The API Settings panel and ?api_url= are for endpoint location only. They should not contain secrets.
Avoid:
https://api.example.org/query-api?token=secret
Prefer:
https://reports.example.org/api/query
Then keep secrets in the backend environment or reverse proxy configuration.
This works well for demos and public static hosting:
https://org.github.io/query/
https://api.example.org/query-api
Requirements:
- API must be HTTPS.
- API must allow the GitHub Pages origin through CORS.
- API Settings should point to the separate API URL.
- Authentication should use a normal login/session flow or a server-side proxy. Do not put secrets in the URL.
For a library or organization intranet:
https://reports.internal.example.org/
https://reports.internal.example.org/api/query
Recommended controls:
- Serve the frontend over HTTPS, even internally.
- Put the API on the same origin or behind the same reverse proxy.
- Restrict API access to the internal network or authenticated users.
- Add rate limits and query timeouts.
- Log action, user, query id, duration, row count, and errors server-side.
- Use the API Settings compatibility check after deployment.
- API Settings compatibility check passes core items: browser access, field metadata, JSONL stream, event order.
runstreamsmeta, zero or morerowevents, anddone.- Multi-value cells are JSON arrays when multiple values exist.
- Optional actions are either implemented or intentionally reported as missing.
- CORS or same-origin routing is configured.
- Trusted
REMOTE_USERauthentication and the administrator allowlist are configured before the hardened CGI is installed. - Unauthenticated, normal-user, administrator, cross-owner, and disallowed-origin checks produce the expected
401,403,200, or404response. - Secrets are server-side only.
npm testpasses before publishing frontend changes.