> For the complete documentation index, see [llms.txt](https://docs.facephi.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.facephi.com/docs.facephi-en/sdks/backend-sdk/iad/technical_documentation/technical_specifications.md).

# Technical specifications

## Minimum requirements

| Component | Requirement                   |
| --------- | ----------------------------- |
| OS        | Linux x86\_64 (Ubuntu 20.04+) |
| CPU       | 4 cores                       |
| Memory    | 4 GB RAM                      |
| Disk      | 1 GB free space               |

## Recommended for production

| Component | Recommendation         |
| --------- | ---------------------- |
| OS        | Ubuntu 24.04 LTS       |
| CPU       | 8+ cores               |
| Memory    | 8 GB+ RAM              |
| Network   | Low-latency connection |
| Storage   | SSD for logs           |

## Network requirements

### Outbound connectivity

The service must be able to reach:

* Facephi license servers (for license validation)

### License server access

**Required IPs and ports:**

| IP           | Port | Protocol |
| ------------ | ---- | -------- |
| 52.223.22.71 | 443  | TCP/IP   |
| 35.71.188.31 | 443  | TCP/IP   |
| 75.2.113.112 | 443  | TCP/IP   |
| 99.83.149.57 | 443  | TCP/IP   |

**Required URLs:**

* `https://api.cryptlex.com:443`
* `https://api.eu.cryptlex.com:443`

Configure firewall rules to allow outgoing HTTPS traffic to these endpoints.

## Deployment architecture

IAD Service operates as a stateless Rest API that evaluates capture payloads and returns a Facephi public contract:

```
Client → IAD Service (Port 6982)
```

* Multiple service instances can run behind a load balancer
* Each instance maintains the internal resources necessary for capture processing
* The service does not store session state

## Public API compatibility

{% hint style="danger" %}
**Breaking change notice (2.0.0)** The public Rest API exposed by version 2.0.0 breaks compatibility with the 1.x.x series. This affects the communication contract, not just the documentation. Integrations must migrate the endpoint routes, the multipart field names, and the response parsing rules for correct responses.
{% endhint %}

| Integration area         | 1.x.x                       | 2.0.0                           |
| ------------------------ | --------------------------- | ------------------------------- |
| Liveness Endpoint        | `/api/v1/iad/check-capture` | `/api/v1/iad/liveness/evaluate` |
| Extraction Endpoint      | `/api/v1/iad/extract-image` | `/api/v1/iad/extract`           |
| Multipart request field  | `file`                      | `capture`                       |
| Correct liveness payload | Legacy private fields       | Facephi public fields           |

## Experimental replay attack mitigation

The service can apply a freshness window to incoming capture payloads. This experimental protection is disabled by default.

* Enable it with `FACEPHI_IAD_REPLAY_ATTACK_CHECKER_ENABLED=true`
* Adjust the freshness window with `FACEPHI_IAD_REPLAY_ATTACK_TOLERANCE_TIME=<seconds>`
* Default freshness window: `300` seconds
* It applies to capture processing endpoints such as `POST /api/v1/iad/liveness/evaluate` and `POST /api/v1/iad/extract`
* When the freshness window is exceeded, the public API returns HTTP `400` with `message` equal to `Replay attack detected`

This functionality is configured at startup using environment variables. It is not part of `config.json` nor exposed through `GET|POST /api/v1/iad/config`.

## JWT authentication

JWT authentication can be enabled at startup via `config.json` or via environment variables `FACEPHI_IAD_REST_AUTH_*`.

* Supported startup keys: `auth_enabled`, `auth_jwt_secret`, `auth_accept_authorization_header`, `auth_accept_api_key_header`, `auth_api_key_header_name`
* `GET /api/v1/iad/config` omits those keys from the public configuration view
* `POST /api/v1/iad/config` rejects those keys and cannot be used to rotate the JWT authentication configuration at runtime

## Performance characteristics

| Metric                 | Typical value                                             |
| ---------------------- | --------------------------------------------------------- |
| Latency                | < 100ms                                                   |
| Throughput             | Depends on service sizing and capture processing capacity |
| Concurrent connections | Limited by `number_of_threads` and `engine_pool_size`     |
| Maximum request size   | Configurable via `client_max_body_size`                   |

## Security considerations

* Deploy behind a reverse proxy or API gateway
* Enable SSL/TLS for all communications
* Prefer injecting `auth_jwt_secret` via environment variables or a secrets manager instead of including it in files
* Protect the license file with the proper permissions (chmod 644)
* Use firewall rules to restrict incoming connections

### Facephi normalized error messages

For `POST /api/v1/iad/liveness/evaluate`capture validation failures are normalized to public values of `message`including:

* `NoneBecauseFaceTooClose`
* `NoneBecauseFaceNotFound`
* `NoneBecauseFaceCropped`
* `NoneBecauseFaceOccluded`
* `NoneBecauseTooManyFaces`
* `NoneBecauseAngleTooLarge`
* `NoneBecauseFaceTooSmall`
* `NoneBecauseFaceTooCloseToBorder`
* `NoneBecauseEyesClosed`
* `NoneBecauseImageDataError`
* `NoneBecauseLicenseError`
* `Replay attack detected`
* `ErrorProcessing`

For `POST /api/v1/iad/extract`payload parsing and decryption failures are returned as `NoneBecauseImageDataError`; expired captures rejected by replay protection return `Replay attack detected`; unclassified extraction failures are returned as `ErrorFacialImage`.

Correct liveness responses are mapped to the public fields `diagnostic`, `reason`, `probability`, `score`, `faceProbability` optional, `sdkDuration` and `queueDuration`. The legacy private fields and the engine's raw payloads are not exposed.

The possible values for `reason` en `POST /api/v1/iad/liveness/evaluate` are:

* `None`
* `Unknown`
* `UntrustedEnvironment`
* `SuspiciousActivity`
* `UntrustedDevice`
* `SdkIntegrityViolation`
* `UntrustedCorruptedPayload`
* `UntrustedContent`
* `UntrustedContentLowConfidence`

The meaning of each public value of `reason` is:

| `reason`                        | Meaning                                                                                                                                              |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `None`                          | The capture was accepted as `Live` and no rejection reason applies.                                                                                  |
| `Unknown`                       | The service could not assign the response to a documented public rejection reason.                                                                   |
| `UntrustedEnvironment`          | The capture was rejected because the execution environment is not considered trusted.                                                                |
| `SuspiciousActivity`            | The capture was rejected because the device showed activity patterns associated with an attack.                                                      |
| `UntrustedDevice`               | The capture was rejected because the device could not be trusted to be what it claims to be.                                                         |
| `SdkIntegrityViolation`         | The capture was rejected because the capture SDK or its libraries appear to have been tampered with.                                                 |
| `UntrustedCorruptedPayload`     | The capture was rejected because the payload appears to be corrupted or manipulated.                                                                 |
| `UntrustedContent`              | The capture was rejected because an injection attack was detected.                                                                                   |
| `UntrustedContentLowConfidence` | The capture was rejected because the service detected signs of an injection attack with lower confidence; it should still be treated as a rejection. |
