---
title: "Private OCR (GCP Document AI)"
slug: private-ocr
section: Integrations
status: stable
description: "Passport OCR runs entirely inside Google Cloud on a private network path."
last-updated: 2026-10-07
---

# Private OCR (GCP Document AI)

Passport OCR runs entirely inside Google Cloud on a private network path. Applicant document PII never traverses the public internet, and no image is sent to a third-party public API.

## Overview

Passport extraction previously called the OpenAI GPT-4o public API, which meant applicant PII left our infrastructure. That dependency is removed. Extraction now runs on a pre-built GCP Document AI ID Proofing processor in a dedicated globus-ocr GCP project, reached over a private network path from the application's GKE cluster. The Globus backend and AI Skills layer call an internal OCR microservice and always receive Globus schema field names — they never speak Document AI directly and never fall back to OpenAI.

> **One private path, no public OCR.**
>
> Every passport image processed by the platform travels only over Google's private backbone via Private Service Connect. The OpenAI passport OCR code path no longer exists.

## What Changed

| Aspect | Before | After (implemented) |
| --- | --- | --- |
| OCR provider | OpenAI GPT-4o (public API) | GCP Document AI ID_PROOFING_PROCESSOR (pre-built) |
| Network path | Public internet to api.openai.com | Private Service Connect to Google backbone |
| Hosting | Third-party SaaS | Dedicated globus-ocr GCP project |
| Caller restriction | None at OCR layer | Kubernetes NetworkPolicy (ocr-service-caller: true) |
| Auth | OpenAI API key | Cross-project Workload Identity (no key files) |
| Language detection | GPT-4o-mini Vision (public) | GCP, returned in the OCR response |
| Error behavior | n/a | Structured error — no OpenAI fallback |

## OCR Architecture

The Document AI processor lives in a dedicated globus-ocr GCP project, separate from the main Globus project that runs the GKE Autopilot application. The two projects are connected by Private Service Connect and cross-project Workload Identity — not VPC peering and not Shared VPC.

Dedicated project: globus-ocr holds the Document AI processor and CMEK key, isolated from the main Globus project. / Private Service Connect: a PSC forwarding rule plus a Cloud DNS private zone for documentai.googleapis.com forces all Document AI SDK calls from GKE pods onto Google's private backbone — no code change required. / Caller restriction: a Kubernetes NetworkPolicy permits egress to the PSC endpoint only from pods labelled ocr-service-caller: true; all other pods are denied. (GKE Autopilot does not support firewall network tags, so NetworkPolicy replaces the original firewall-tag design.) / Processor: pre-built ID_PROOFING_PROCESSOR for identity documents — not a Custom Extractor or Gemini model. / Field mapping: the internal OCR microservice maps Document AI native field names to Globus schema names before responding. / Encryption & residency: data is encrypted with the customer-managed key globus-docai-cmek (90-day rotation) and processed in the us multi-region. / Infrastructure repo: globus-ocr-infra holds the Terraform/manifests for these resources.
The same flow is rendered in the master diagram on the Architecture Diagram page under Integration Systems.

## Consumed Dependency (Ecosystem Audit Record)

This entry is the ecosystem-level source of truth for the OCR microservice dependency. The MCP ecosystem true-up audit derives its "not implemented" verdicts from MCP registry presence per portal. POST /api/v1/ocr/passport is the path of the external internal-GCP Document AI OCR microservice that the Globus Hub (portal 2) calls outbound — it is not a registered ecosystem portal and Globus is the consumer, not the provider. The path therefore correctly does not appear among Globus's published portal-2 endpoints, and must be classified as a consumed dependency rather than a missing Globus endpoint.

| Attribute | Value |
| --- | --- |
| Consumed path | POST /api/v1/ocr/passport |
| Classification | External internal-GCP OCR microservice — consumed dependency |
| Provider | Internal GCP Document AI OCR microservice (not a registered ecosystem portal) |
| Consumer | Globus Hub (portal 2), outbound |
| Hub-side caller | server/services/gcpDocumentAiAdapter.ts (base URL OCR_SERVICE_BASE_URL, bearer INTERNAL_OCR_API_KEY) |
| Canonical Globus capability | POST /api/extract-passport-data — Globus-owned, published to the portal-2 registry |
| Audit expectation | Treat /api/v1/ocr/passport as a consumed dependency; do not flag it as a missing Globus endpoint |

> **Mapping: consumed path → canonical Globus capability.**
>
> The external POST /api/v1/ocr/passport maps to the canonical Globus-owned capability POST /api/extract-passport-data. The canonical capability is, and should remain, the route published to the portal-2 MCP registry. The external path stays absent from that registry by design — recording it here, not registering it, is the correct resolution. This is documentation and registry metadata only: it registers no new Globus HTTP route and changes no passport OCR runtime behaviour.

## Endpoint Contract

The internal OCR microservice exposes a single endpoint. The Globus applicant-portal backend calls it in place of the former OpenAI call; the applicant review-and-confirm flow is unchanged (extracted fields pre-populate the form and remain editable before submission).

### Request

### Response

Field names are Globus schema names. The microservice has already mapped them from the Document AI native names. A per-field confidence object accompanies every extraction; fields below the confidence threshold are flagged for human review in the applicant flow.

> **No fallback on failure.**
>
> If the Document AI endpoint is unavailable the microservice returns a structured error. The Globus backend surfaces that error — it never silently falls back to OpenAI for this code path.

## Field Name Mapping

Document AI returns native field names. The internal OCR microservice maps them to Globus schema names. The Globus backend and AI Skills layer only ever work with the Globus names — do not reimplement this mapping downstream.

| Document AI native | Globus schema |
| --- | --- |
| given_name | given_names |
| family_name | surname |
| nationality | nationality |
| document_id | passport_number |
| date_of_birth | date_of_birth |
| expiration_date | expiry_date |
| issuing_country | issuing_country |

## AI Skill — passport-ocr-gcp

The Globus AI Skills registry defines a passport_extraction_gcp capability that uses the private provider. It replaces the deprecated OpenAI OCR skill and routes through the model-provider abstraction layer (see Capability Layers — AI Integration).

provider: PRIVATE_GCP, processor_type: ID_PROOFING_PROCESSOR. / Invokes the GCPDocumentAIAdapter; receives already-mapped Globus schema field names. / Input: file_uri (readable by docai-ocr-sa via roles/storage.objectViewer) + mime_type. / Error policy: return a structured error; never fall back to OpenAI under any condition. / The legacy OpenAI OCR skill is deprecated.
## Language Detection

Automated language detection previously used GPT-4o-mini Vision, which also sent document images to a public OpenAI endpoint. It is migrated to a private GCP-based solution and returned as the detected_languages field on the OCR response. The AI Skills language-detection skill reads that field rather than calling OpenAI; the nine currently supported language pairs are preserved with equivalent or better accuracy. Note: the criminal-background workflow's mandatory translation set (see Criminal Background Translation — Supported Translation Languages) is a product-governance commitment defined in that specification; it is not derived from this service's internal pair list.

## Translation Skill — translate

Free-text translation is not a Globus Hub-hosted route. POST /api/v1/translate is the contract of an external internal GCP translation microservice; the Globus Hub is an outbound client of that contract, calling it through its translateViaPrivateGcp() adapter rather than hosting the endpoint itself. Sensitive applicant text (GDPR Art. 10 special-category data) stays on Globus infrastructure and never traverses a public translation API. Capability execution selects this path via the model-provider abstraction layer (see Capability Layers — AI Integration).

### External microservice contract

The request/response shapes below describe the external GCP microservice that the Hub calls. They are documented here for reference; the Hub does not implement or serve this route.

### Hub-side integration surface

This is the part of the architecture that actually lives on the Globus Hub repo — the outbound adapter, its provider routing string, and the super-admin diagnostics endpoints used to verify the dependency.

Outbound adapter translateViaPrivateGcp(): env-gated on TRANSLATION_SERVICE_URL with optional INTERNAL_TRANSLATION_API_KEY bearer auth on the outbound call. / Returns structured, discriminated errors on failure — and never falls back to OpenAI or any public translation API (by design; GDPR Art. 10 data must stay on Globus infrastructure). / Provider routing string PRIVATE_GCP_TRANSLATION is wired into capability execution to select this outbound path. / Hub-hosted, super-admin-only diagnostics: GET /api/admin/diagnostics/translation/config reports adapter configuration; POST /api/admin/diagnostics/translation runs an on-demand round-trip probe.
> **Dormant by default.**
>
> With TRANSLATION_SERVICE_URL unset, the integration short-circuits and returns TRANSLATION_NOT_CONFIGURED instantly — no network call is made. The live round-trip against the external microservice is verified on the GCP dev environment, not inside the Replit env.

> **No fallback on failure.**
>
> If the external translation microservice is unavailable the adapter returns a structured error. The Globus Hub surfaces that error—it never silently falls back to a public translation API for this code path, because GDPR Art. 10 special-category data must stay on Globus infrastructure.

## Auth, Deployment & Config

| Concern | Requirement |
| --- | --- |
| Auth | Cross-project Workload Identity: docai-ocr-ksa (KSA) → docai-ocr-sa@globus-ocr.iam.gserviceaccount.com. No service-account key files. |
| Network | The OCR microservice pod must carry the label ocr-service-caller: true, or the NetworkPolicy denies its egress to Document AI. |
| Processor ID secret | globus-development-docai-processor-id (Secret Manager). |
| Internal OCR API key secret | globus-development-internal-ocr-api-key (Secret Manager) — also holds the internal endpoint URL the Globus backend reads. |
| IAM | Only docai-ocr-sa holds roles/documentai.apiUser; reads input from storage via roles/storage.objectViewer. |

## VPC-SC Status

VPC Service Controls are deferred pending establishment of a GCP organization. The Phase 1 security posture relies on Private Service Connect, Kubernetes NetworkPolicy, and least-privilege IAM. The residual risk this leaves open — and the remediation path — are documented in the compliance ledger.

## Compliance

The formal PII-boundary attestation, control evidence, residual-risk register, and CMEK rotation schedule live in the OCR Data Privacy & Compliance Ledger. Broader platform PII rules are in PII & Data Handling.
