Skip to content

Web app and API

corpusos web serves three things from one process:

Path What
/ the web app: Search, Library, Tables, and for admins Storage, Review and Jobs
/api/… the HTTP API; interactive reference at /api/reference
/docs/ this documentation, when the site is built (CORPUSOS_DOCS_DIR)
/health liveness check (/healthz too, except on Cloud Run, which reserves it)
corpusos web --host 0.0.0.0 --port 8080 -C /path/to/corpus      # PORT is read from the environment too

Sign in

Set web.auth in config/security.yaml, or the CORPUSOS_WEB_AUTH environment variable.

Mode Use it for How it works
header local development Trusts the X-CorpusOS-Principal header. Refused on anything but loopback.
firebase Google, Microsoft, email or SAML sign-in through Firebase Auth or Google Identity Platform The browser signs in with the Firebase SDK. The server exchanges the fresh ID token for an HttpOnly session cookie and verifies that cookie on every request, including the revocation check.
jwt an authenticating proxy (Google IAP, oauth2-proxy, an API gateway) Verifies the proxy's signed token against the issuer's JWKS, issuer and audience.
web:
  auth: firebase
  public_url: https://corpus.example.com
  admins: ${CORPUSOS_ADMINS}               # comma-separated emails
  allowed_domains: example.com             # who may sign in at all; empty = any verified email
  allowed_emails: ""
  principal_map: {alice@example.com: [finance]}   # email -> extra principals (groups)
  firebase:
    project_id: ${CORPUSOS_FIREBASE_PROJECT_ID}
    api_key: ${CORPUSOS_FIREBASE_API_KEY}  # the public web API key: it identifies the project, grants nothing
    auth_domain: ${CORPUSOS_FIREBASE_AUTH_DOMAIN}
    tenant_id: ""                          # Identity Platform tenant, if you use one
    session_days: 5

For Firebase:

  • Add your site's domain to Authentication › Settings › Authorized domains.
  • Give the server's service account permission to create session cookies and read users, for example roles/firebaseauth.admin on the Firebase project.
  • Install the firebase extra.

For Google IAP:

web:
  auth: jwt
  jwt:
    header: x-goog-iap-jwt-assertion
    issuer: https://cloud.google.com/iap
    audience: /projects/<project-number>/locations/<region>/services/<service>
    jwks_url: https://www.gstatic.com/iap/verify/public_key-jwk
    algorithms: [ES256]
    principal_claim: email

Your principals are your verified email plus anything principal_map adds. Every read is checked against each document's live access list. Being an admin adds upload, delete, sync and review rights, but no extra read rights.

Cross-site request protection

Session cookies are HttpOnly, Secure and SameSite=Strict. Any write made with a cookie must also send the header X-CorpusOS-Request: 1. The web app sends it for you.

The API

For scripts: API keys

corpusos mcp-key create -p me@example.com --label uploader     # prints the key once, plus its SHA-256
web:
  api_keys:
    - {label: uploader, sha256: <sha256>, principals: [me@example.com], admin: true}

Send it as X-API-Key: <key> or Authorization: Bearer <key>. Only the hash is stored. In firebase mode, a script can also send a Firebase ID token as Authorization: Bearer <id-token>.

Reading

Method and path Returns
GET /api/search?q=…&limit=8 routed search: passages with citations and an evidence_status of sufficient or insufficient
GET /api/library the current documents you may read
GET /api/documents/{document_id} the canonical document
GET /api/evidence/{chunk_id}?expand=section a passage with its section, or with its neighbours
GET /api/documents/{id}/compare what changed between revisions
GET /api/lineage/{id} how a document or passage was produced
GET /api/tables · POST /api/sql typed tables and read-only SQL
GET /api/me who you are signed in as

Writing (admins)

Method and path Does
POST /api/storage/{source} uploads files (multipart), with an optional paths per file to keep folders, then queues a sync
DELETE /api/storage/{source}/{path} removes a file from storage; the sync then deletes it from the corpus
POST /api/sync?source=… ingests one source folder, or all of them
GET /api/storage · GET /api/jobs · GET /api/jobs/{id} storage contents and ingest jobs
GET /api/review · GET /api/review/{rev}/content the review queue and held content
POST /api/review/{rev}/approve · /reject publishes a held revision, or keeps it out for good
GET /api/admin/status corpus counts
KEY=cos_...
curl -H "X-API-Key: $KEY" -F files=@handbook.pdf -F files=@budget.xlsx https://corpus.example.com/api/storage/hr
curl -H "X-API-Key: $KEY" https://corpus.example.com/api/jobs/job_1a2b3c4d5e6f
curl -H "X-API-Key: $KEY" "https://corpus.example.com/api/search?q=parental+leave"

Writes return 202 together with a job. Ingest runs in the background, one job at a time, so a long OCR run doesn't hold a request open.