> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reducto.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# OCR provider configuration

> Configure cloud OCR providers for on-premise Reducto installations

export const PasswordProtect = ({children}) => {
  const [password, setPassword] = useState("");
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [error, setError] = useState("");
  const correctPasswordHash = "9daff39ca2584edc54444193f62e5e54dce0bcd5e5d604b1748c79bfb3d7d1fd";
  const hashPassword = async inputPassword => {
    const encoder = new TextEncoder();
    const data = encoder.encode(inputPassword);
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
  };
  useEffect(() => {
    const storedPassword = localStorage.getItem("reducto-onprem-password");
    if (storedPassword) {
      checkStoredPassword(storedPassword);
    }
  }, []);
  const checkStoredPassword = async storedPassword => {
    const hashedStored = await hashPassword(storedPassword);
    if (hashedStored === correctPasswordHash) {
      setIsAuthenticated(true);
      setError("");
    }
  };
  const handleSubmit = async e => {
    e.preventDefault();
    const hashedInput = await hashPassword(password);
    if (hashedInput === correctPasswordHash) {
      setIsAuthenticated(true);
      setError("");
      localStorage.setItem("reducto-onprem-password", password);
    } else {
      setError("Incorrect password. Please try again.");
      setPassword("");
    }
  };
  if (isAuthenticated) {
    return <>{children}</>;
  }
  return <div style={{
    padding: "2rem",
    border: "2px solid #e2e8f0",
    borderRadius: "8px",
    textAlign: "center",
    margin: "2rem 0"
  }}>
      <div style={{
    fontSize: "2rem",
    marginBottom: "1rem"
  }}>🔒</div>
      <h2>Protected Content</h2>
      <p>This content requires a password to access.</p>
      <form onSubmit={handleSubmit} style={{
    marginTop: "1rem"
  }}>
        <input type="password" value={password} onChange={e => setPassword(e.target.value)} placeholder="Enter password" style={{
    padding: "0.5rem",
    border: "1px solid #cbd5e0",
    borderRadius: "4px",
    marginRight: "0.5rem",
    fontSize: "1rem"
  }} />
        <button type="submit" style={{
    padding: "0.5rem 1rem",
    backgroundColor: "#5c0c5c",
    color: "white",
    border: "none",
    borderRadius: "4px",
    cursor: "pointer",
    fontSize: "1rem"
  }}>
          Unlock
        </button>
      </form>
      {error && <p style={{
    color: "red",
    marginTop: "1rem"
  }}>{error}</p>}
    </div>;
};

<PasswordProtect>
  Reducto supports multiple cloud OCR providers. By default, Reducto uses its own local OCR models (which require GPU), but you can configure cloud OCR providers for broader language support or to avoid provisioning GPU hardware for OCR.

  ## Provider overview

  | Provider              | Credentials needed                                                                                         | Best for                                                                              |
  | --------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
  | **Reducto local OCR** | None (default)                                                                                             | General-purpose, privacy-sensitive, GPU-equipped deployments                          |
  | **AWS Textract**      | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`                                                               | AWS-native deployments                                                                |
  | **Azure Vision Read** | `AZURE_VISION_ENDPOINT` + `AZURE_VISION_KEY` (or `AZURE_VISION_ARRAY`)                                     | Azure-native deployments                                                              |
  | **GCP Vision API**    | `GOOGLE_APPLICATION_CREDENTIALS` + `GCP_PROJECT_ID` (or `GCP_SERVICE_ACCOUNT_EMAIL` for workload identity) | GCP-native deployments, cross-cloud deployments wanting GCP OCR, 60+ language support |

  ## How OCR provider is selected

  The `ocr_system` parameter in API requests controls provider routing. Only two values are available to API callers: `standard` (default) and `legacy`.

  **`standard` (default) routing priority:**

  1. GCP Vision API, if GCP credentials are configured
  2. Azure Vision or AWS Textract, if configured (Azure is preferred over Textract)

  **`legacy` routing priority:**

  1. Azure Vision or AWS Textract, if configured (Azure is preferred over Textract)
  2. GCP Vision API, as fallback if only GCP credentials are available

  In practice: if you configure GCP credentials, `standard` requests use GCP Vision. If you only have Azure/AWS credentials, both `standard` and `legacy` use those.

  ### Auto-detection: GCP-only environments

  When **only** GCP credentials are configured (no AWS or Azure), Reducto auto-detects this and routes all OCR through GCP Vision regardless of the `ocr_system` value. This requires:

  * No Azure credentials (`AZURE_VISION_ENDPOINT` and `AZURE_VISION_ARRAY` both unset)
  * No AWS credentials (`AWS_ACCESS_KEY_ID` unset)
  * GCP credentials available (`GOOGLE_APPLICATION_CREDENTIALS` or `GCP_SERVICE_ACCOUNT_EMAIL`)
  * `GCP_PROJECT_ID` is set

  ***

  ## AWS Textract

  ### Environment variables

  | Variable                | Description                                             | Required |
  | ----------------------- | ------------------------------------------------------- | -------- |
  | `AWS_ACCESS_KEY_ID`     | AWS access key ID                                       | Yes      |
  | `AWS_SECRET_ACCESS_KEY` | AWS secret access key                                   | Yes      |
  | `TEXTRACT_REGIONS`      | Comma-separated `region:quota` pairs for load balancing | No       |

  Default regions: `us-east-2:100,us-east-1:100,us-west-2:100,ap-south-1:5,eu-west-1:5`

  ```bash theme={null}
  # Format: region:quota pairs (quota defaults to 1 if omitted)
  TEXTRACT_REGIONS=us-east-1:50,us-west-2:25,eu-west-1:10

  # Government Cloud
  TEXTRACT_REGIONS=us-gov-west-1:10,us-gov-east-1:10
  ```

  ### Required IAM permissions

  ```json theme={null}
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": "textract:DetectDocumentText",
        "Resource": "*"
      }
    ]
  }
  ```

  ***

  ## Azure Vision Read

  ### Environment variables

  **Single endpoint:**

  | Variable                | Description                        | Required                                |
  | ----------------------- | ---------------------------------- | --------------------------------------- |
  | `AZURE_VISION_ENDPOINT` | Azure Computer Vision endpoint URL | Yes (unless using `AZURE_VISION_ARRAY`) |
  | `AZURE_VISION_KEY`      | Azure Computer Vision API key      | Yes (unless using `AZURE_VISION_ARRAY`) |

  **Multiple endpoints (load balancing and failover):**

  | Variable                      | Description                                               | Required |
  | ----------------------------- | --------------------------------------------------------- | -------- |
  | `AZURE_VISION_ARRAY`          | JSON array of `{"endpoint": "...", "key": "..."}` objects | No       |
  | `AZURE_VISION_ARRAY_STRATEGY` | `load_balance` (default) or `priority`                    | No       |

  ```bash theme={null}
  AZURE_VISION_ARRAY='[
    {"endpoint": "https://vision-east.cognitiveservices.azure.com/", "key": "<key-1>"},
    {"endpoint": "https://vision-west.cognitiveservices.azure.com/", "key": "<key-2>"}
  ]'
  ```

  | Strategy                 | Behavior                                                                                       |
  | ------------------------ | ---------------------------------------------------------------------------------------------- |
  | `load_balance` (default) | Randomly selects an endpoint per request. On transient error, retries then fails over to next. |
  | `priority`               | Tries endpoints in order. On transient error, retries then fails over to next.                 |

  **Timeout and retry tuning:**

  | Variable                          | Default         | Description                                                      |
  | --------------------------------- | --------------- | ---------------------------------------------------------------- |
  | `AZURE_VISION_TOTAL_CALL_TIMEOUT` | `45` (seconds)  | Hard cancellation cap per `analyze` call, including all retries. |
  | `AZURE_VISION_WALL_TIMEOUT`       | `120` (seconds) | SDK-level wall-clock timeout (checked between retries).          |
  | `AZURE_VISION_CONNECTION_TIMEOUT` | `10` (seconds)  | Per-attempt TCP connect timeout.                                 |
  | `AZURE_VISION_READ_TIMEOUT`       | `30` (seconds)  | Per-attempt socket read timeout.                                 |
  | `AZURE_VISION_MAX_RETRIES`        | `2`             | Retries per endpoint. Total attempts = `1 + MAX_RETRIES`.        |

  Transient errors (408, 429, 5xx, network errors) trigger retries and failover. Non-retryable 4xx errors surface immediately.

  <Note>
    When `AZURE_VISION_ARRAY` is set, `AZURE_VISION_ENDPOINT` and `AZURE_VISION_KEY` are ignored.
  </Note>

  ***

  ## GCP Vision API

  ### Environment variables

  | Variable                         | Description                                                                                                                                                           | Required                                       |
  | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
  | `GOOGLE_APPLICATION_CREDENTIALS` | Path to service account key JSON file, or the raw JSON content. Handles both OCR routing detection and Vision API authentication via Application Default Credentials. | Yes (unless using `GCP_SERVICE_ACCOUNT_EMAIL`) |
  | `GCP_PROJECT_ID`                 | GCP project ID for quota attribution                                                                                                                                  | Yes                                            |
  | `GCP_SERVICE_ACCOUNT_EMAIL`      | Service account email for workload identity auth (alternative to `GOOGLE_APPLICATION_CREDENTIALS`).                                                                   | No                                             |
  | `GCP_API_KEY`                    | [API key](https://console.cloud.google.com/apis/credentials) with Cloud Vision API enabled. Optional; if set, the Vision API client uses this instead of ADC.         | No                                             |
  | `GCP_REGION`                     | Region for Vertex AI                                                                                                                                                  | No (default: `us-central1`)                    |

  ### Authentication methods

  **Option 1: Service account key (recommended)**

  `GOOGLE_APPLICATION_CREDENTIALS` handles everything: Reducto uses it to detect GCP availability for routing, and the Vision API client picks it up via Application Default Credentials (ADC).

  ```bash theme={null}
  GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-key.json
  GCP_PROJECT_ID=your-project-id
  ```

  `GOOGLE_APPLICATION_CREDENTIALS` can be a file path or the raw JSON content of the service account key.

  **Option 2: Workload identity (GKE)**

  Set `GCP_SERVICE_ACCOUNT_EMAIL` to use GKE workload identity. No key file needed.

  ```bash theme={null}
  GCP_SERVICE_ACCOUNT_EMAIL=ocr-sa@your-project.iam.gserviceaccount.com
  GCP_PROJECT_ID=your-project-id
  ```

  ### Required GCP API and roles

  * **Cloud Vision API** must be enabled on the project (`GCP_PROJECT_ID`).
  * If using a service account, it needs at minimum the `roles/cloudvision.user` role.
  * If using `GOOGLE_APPLICATION_CREDENTIALS` for broader GCP features (storage, Vertex AI), the service account also needs:
    * `roles/aiplatform.user` (for Vertex AI / Gemini LLM calls)
    * `roles/storage.objectAdmin` (if using GCS for file storage)

  ***

  ## Cross-cloud OCR: using GCP Vision on non-GCP infrastructure

  If you run on Azure or AWS but want GCP Vision for OCR, set `GCP_OCR_ONLY=true` so Reducto uses GCP only for Vision API calls and does not initialize GCS storage.

  | Variable       | Description                                       | Default |
  | -------------- | ------------------------------------------------- | ------- |
  | `GCP_OCR_ONLY` | Use GCP for Vision API OCR only, not for storage. | `false` |

  ### Example: Azure infrastructure with GCP Vision OCR

  ```bash theme={null}
  # --- Azure storage (unchanged) ---
  AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=...

  # --- Azure Vision (optional, keep as fallback or remove) ---
  # If you want to remove Azure Vision entirely, delete these.
  # If you keep them, 'legacy' OCR requests will still use Azure Vision,
  # while 'standard' (default) requests will use GCP Vision.
  AZURE_VISION_ENDPOINT=https://your-vision.cognitiveservices.azure.com/
  AZURE_VISION_KEY=your-azure-vision-key

  # --- GCP Vision OCR ---
  GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-key.json
  GCP_PROJECT_ID=your-gcp-project-id
  GCP_OCR_ONLY=true
  ```

  With this configuration:

  * **Default API requests** (`ocr_system=standard` or unset) route to **GCP Vision API**
  * **`ocr_system=legacy` requests** route to **Azure Vision** (if Azure Vision credentials are still set) or **GCP Vision** (if Azure Vision credentials are removed)
  * **File storage** remains on **Azure Blob Storage**

  ### Example: AWS infrastructure with GCP Vision OCR

  ```bash theme={null}
  # --- AWS storage ---
  AWS_ACCESS_KEY_ID=your-aws-key
  AWS_SECRET_ACCESS_KEY=your-aws-secret
  BUCKET=your-s3-bucket

  # --- GCP Vision OCR ---
  GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-key.json
  GCP_PROJECT_ID=your-gcp-project-id
  GCP_OCR_ONLY=true
  ```

  ### Migration path: switching from Azure Vision to GCP Vision

  If you are currently using Azure Vision and want to switch to GCP Vision as your primary OCR:

  1. **Add GCP credentials** to your deployment:
     ```bash theme={null}
     GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account-key.json
     GCP_PROJECT_ID=your-gcp-project-id
     GCP_OCR_ONLY=true
     ```

  2. **Test with a few requests.** The default `ocr_system=standard` will now route to GCP Vision. Verify OCR quality meets your expectations.

  3. **Optionally remove Azure Vision credentials.** If you no longer need Azure Vision as a fallback for `legacy` requests, remove `AZURE_VISION_ENDPOINT` and `AZURE_VISION_KEY` (or `AZURE_VISION_ARRAY`). This simplifies the configuration.

  4. **`GCP_OCR_ONLY` should remain set** as long as you are using non-GCP file storage.

  ***

  ## GPU OCR deployment (`OCR_ONLY` mode)

  For deployments that want to use Reducto's own OCR models (which run on GPU) as a dedicated service, set `OCR_ONLY=true`. This starts a lightweight service exposing only the `/ocr` endpoint.

  | Variable                  | Description                                 | Default |
  | ------------------------- | ------------------------------------------- | ------- |
  | `OCR_ONLY`                | Enable OCR-only mode                        | `false` |
  | `OFFLINE`                 | Must be `1` for on-prem deployments         | -       |
  | `HTTP_WORKERS`            | HTTP worker processes                       | `8`     |
  | `MAX_PROCESSING_REQUESTS` | Max concurrent OCR requests per worker      | `1`     |
  | `MAX_QUEUED_REQUESTS`     | Max queued requests per worker              | `0`     |
  | `OCR_THREADS`             | Thread pool size for OCR                    | `5`     |
  | `NUM_GPUS`                | GPUs available (for CUDA device assignment) | `0`     |

  ```yaml theme={null}
  # Helm values
  gpuOcr:
    enabled: true
    replicaCount: 1
    resources:
      requests:
        nvidia.com/gpu: 1
      limits:
        nvidia.com/gpu: 1
  ```

  The Helm chart sets `OCR_ONLY=true` and `OFFLINE=1` automatically.
</PasswordProtect>
