# File Inspection Engine Documentation > Containerized file analysis service documentation. This file contains the documentation for one product, following the llmstxt.org standard. ## Shared documentation The concepts, classification rules, deployment requirements, and security configuration shared across all ReversingLabs products are in a separate file. Read it alongside this one: - [General concepts and reference](https://docs.reversinglabs.com/llms-general.txt) ## Spectra Core lookup tables These reference tables aren't included here — each is a large value-to-description table. Fetch one when you need to resolve a specific value: - [Spectra Core indicators](https://docs.reversinglabs.com/General/SpectraCore/indicators.md) — indicator ID to the behavior it describes - [Security grades and codes](https://docs.reversinglabs.com/General/SpectraCore/grade-codes.md) — security grades (A-F) and RC, WC, and SC codes - [Spectra Core certificate trust store](https://docs.reversinglabs.com/General/SpectraCore/cert-trust-store.md) — trusted certificates with serial numbers and thumbprints - [Spectra Core tags](https://docs.reversinglabs.com/General/SpectraCore/core-tags.md) — tag name to what it means ## File Inspection Engine File Inspection Engine (FIE) is a containerized file analysis service that performs synchronous, real-time scanning of files via an HTTP API. It is designed for integration into network security pipelines where files must be inspected inline — each request submits a file, waits for analysis to complete, and receives a verdict in the same response. FIE uses [Spectra Core](/General/AnalysisAndClassification/SpectraCoreAnalysis) for static file analysis, enabling deep inspection of over 400 file formats without executing files. It is deployed as an OCI-compliant container on Docker or Kubernetes and maintains a local threat database, so file content never leaves your infrastructure during scanning. ## Key capabilities - Synchronous HTTP API — submit a file, receive a classification verdict in one request - Containerized deployment on Docker or Kubernetes (no agent installation required) - Local threat database — all file analysis happens on-premises - Configurable Spectra Core instances for throughput scaling - Large file handling with dedicated core pools - Optional enrichment with cloud threat details via [Spectra Intelligence](/SpectraIntelligence/) hash lookups (hash only, no file upload) ## Privacy File Inspection Engine keeps all file data on-premises. Files submitted for scanning are processed locally using a bundled threat database and are not uploaded to external services. When the `--with-threat-details` option is enabled, FIE contacts Spectra Intelligence using the file hash only — the file itself is never transmitted. The threat database is updated on a regular schedule from ReversingLabs infrastructure. ## Deployment options Choose a deployment model based on your infrastructure: - [Deployment Overview and Hardware Requirements](./Deployment/) — capacity planning, Spectra Core instance sizing, and networking prerequisites - [Docker Deployment](./Deployment/docker.md) — standalone container setup for development and smaller deployments - [Kubernetes Deployment](./Deployment/kubernetes.md) — Helm chart installation for production Kubernetes clusters - [Air-Gapped Kubernetes](./Deployment/air-gapped-kubernetes.md) — deployment in offline or restricted network environments - [Helm Values Reference](./Deployment/Examples/values.md) — complete chart configuration reference ## Configuration FIE is configured via CLI flags and environment variables. Key settings include the number of Spectra Core instances, analysis timeouts, maximum file size, and network interface bindings. See the [Configuration Reference](./configuration.md) for all available options. ## Usage and API FIE exposes a REST API for file submission, result retrieval, status monitoring, and classification overrides. See the [Usage Guide](./usage.md) for API endpoint documentation, scanning workflows, response formats, and error handling. ## Related resources - [Spectra Core Analysis](/General/AnalysisAndClassification/SpectraCoreAnalysis) — how the underlying static analysis engine works - [Classification](/General/AnalysisAndClassification/Classification) — risk scores, threat levels, and classification methodology - [Platform Requirements](/General/DeploymentAndIntegration/PlatformRequirements) — hardware sizing across all ReversingLabs products --- ## Getting started with File Inspection Engine This guide walks you through running File Inspection Engine (FIE) locally with Docker and scanning your first file via the HTTP API. **What you'll accomplish:** - Pull and run the FIE container - Verify the engine is ready - Submit your first file for analysis - Understand the classification response ## Prerequisites Before you begin: - Docker installed and running on your machine - A ReversingLabs license file for FIE (`.lic`) - Your ReversingLabs cloud credentials (username and password) for registry access - `curl` installed :::tip[Obtaining your license and credentials] Your FIE license file and cloud credentials are provided by [ReversingLabs Support](mailto:support@reversinglabs.com) when your FIE subscription is activated. If you do not have them, contact support before proceeding. ::: ## Step 1: Pull the container image Log in to the ReversingLabs container registry using your cloud username and password, then pull the FIE image: ```bash docker login registry.reversinglabs.com docker pull registry.reversinglabs.com/fie/file-inspection-engine:latest ``` ## Step 2: Start the container Run FIE with your license passed as an environment variable: ```bash docker run -d \ --name fie \ -p 8000:8000 \ -e RL_LICENSE="$(cat /path/to/rl-license.lic)" \ registry.reversinglabs.com/fie/file-inspection-engine:latest ``` On first start, FIE downloads its threat database. Wait for the container to become ready: ```bash curl http://localhost:8000/readyz ``` When ready, this returns `200 OK`. The download may take a few minutes depending on your network speed. ## Step 3: Scan your first file Submit a file for analysis using the `/scan` endpoint: ```bash curl -X POST --upload-file /path/to/your/sample.exe \ http://localhost:8000/scan ``` FIE analyzes the file synchronously and returns a JSON verdict in the same response. ## Step 4: Interpret the response A typical response looks like: ```json { "classification": "OK", "message": "", "errors": [] } ``` The `classification` field indicates the verdict: | Value | Meaning | | --- | --- | | `OK` | File is goodware or unknown — no threat detected | | `malicious` | File is classified as malicious | | `suspicious` | File shows suspicious indicators (only when paranoid mode is enabled) | ### Getting additional threat details To get threat details alongside the classification verdict, start FIE with the `--with-threat-details` and `--add-file-type` flags. The enriched response includes the threat name, platform, and file type: ```json { "classification": "malicious", "message": "", "errors": [], "threat_details": { "platform": "Script", "type": "Trojan", "threat_name": "Script-JS.Trojan.Redirector" }, "file_type": "Text" } ``` ## Next steps Now that you've scanned your first file, explore the full capabilities of FIE: - **[Usage Guide](./usage.md)** — Complete API reference including all endpoints, hash lookups, status monitoring, error handling, and classification overrides - **[Configuration Reference](./configuration.md)** — CLI flags for core instances, timeouts, file size limits, and advanced options - **[Kubernetes Deployment](./Deployment/kubernetes.md)** — Deploy FIE at scale on Kubernetes - **[Air-Gapped Deployment](./Deployment/air-gapped-kubernetes.md)** — Offline environment setup For troubleshooting common issues or understanding response codes, see the [Usage Guide](./usage.md#possible-response-status-codes). --- ## File Inspection Engine Configuration Reference The `--help` flag output lists all available command-line options along with their default values.**Note that default values may be overridden by environment variables.** - **Boolean flags** must include an equals sign (=) when explicitly set to `true`/`1` or `false`/`0` (e.g., `--cloud-updates=true`, `--cloud-updates=false`). Alternatively, to enable a boolean flag, use the shortcut by specifying the flag name alone (e.g., `--cloud-updates` for `true`). Non-boolean flags don't need an equals sign. Both `--timeout 10s` and `--timeout=10s` are fine. - **Time Duration Options:** For configuration options containing time durations, the supported suffixes are `ms`, `s`, `m`, and `h` (e.g., `--timeout 10s` or `--cloud-update-interval 1m`). **Info: **Environment variables:** Command line flags can also be passed as environment variables, by using `RL_` as the prefix and replacing the dashes in between the words with underscores. For example, `--http-address` becomes the environment variable `RL_HTTP_ADDRESS`.** When a default value is not mentioned in the help output, it is empty (either an empty string or `false` for boolean options). ## Configuration options ### `RL_LICENSE` - **Description**: Set the contents of your license file. - **Default**: N/A - **Notes**: This option is **only** available as an environment variable. --- ### `RL_RETRY_COUNT` - **Description:** Configures the maximum number of retries for failed threat data segment downloads when using the `threat-data` command. - **Default:** 3 - **Possible Values:** 0 to 100 --- ### `--add-file-type` / `RL_ADD_FILE_TYPE` - **Description**: Controls whether `file_type` information is returned in the [`/scan` API response](./usage.md#file-submissions). - **Default**: `disabled` - **Possible Values**: `disabled`, `enabled`, `force` - **Notes**: - When `enabled`, the correct `file_type` will only be returned if static analysis was performed. - When `force` is set, static analysis is always performed. --- ### `--concurrency-limit` / `RL_CONCURRENCY_LIMIT` - **Description**: Maximum concurrent requests when performing file analysis, across all HTTP endpoints. - **Default**: 20 - **Possible Values**: From `0` (unlimited) to `100` - **Notes**: Even if the limit is set to 0 (unlimited), the system will still track the number of active concurrent requests. The `active_concurrency` field will always reflect the actual number of concurrent requests, regardless of the limit setting. The `active_concurrency` field is logged in the format: `active_concurrency={value}/{limit}`. --- ### `--cpu-request` / `RL_CPU_REQUEST` - **Description**: Informs the application how many CPUs were requested for the container. This value does not control how many Spectra Core instances are created. Those are configured explicitly with `--number-of-regular-cores` and `--number-of-large-cores`. Instead, it is used only for reporting in the `/status` endpoint. - **Default**: Not set (FIE will use the total number of CPUs detected on the node). - **Notes**: - When set, the `percentage_*` fields in the `/status` endpoint are calculated relative to this value. - The `available_*` fields show counts of available instances and are not affected by this value. - This option is most useful in Kubernetes, where you typically specify `resources.requests.cpu`. Docker does not have a concept of CPU requests. - You can provide the value in three ways: 1. Command-line flag: ```yaml args: ["--cpu-request=8"] ``` 2. Environment variable: ```yaml env: - name: RL_CPU_REQUEST value: "8" ``` 3. Kubernetes Downward API (avoids duplicating the number): ```yaml env: - name: RL_CPU_REQUEST valueFrom: resourceFieldRef: containerName: fie resource: requests.cpu ``` --- ### `--proxy-address` / `RL_PROXY_ADDRESS` - **Description**: Specifies the address of a proxy server for contacting the cloud API. - **Default**: N/A - **Possible Values**: - `https://host:port` - `http://host:port` - `socks5://host:port` - **Notes**: You can include credentials in the proxy URL, for example: - `http://user:password@localhost:8080` --- ### `--cloud-update-interval` / `RL_CLOUD_UPDATE_INTERVAL` - **Description**: Sets how frequently the application checks for cloud threat data updates. - **Default**: `5m` - **Possible Values**: From `1m` to `24h` (e.g., `45m`, `6h`) --- ### `--cloud-update-concurrency` / `RL_CLOUD_UPDATE_CONCURRENCY` - **Description**: The number of concurrent network connections used during cloud threat data updates. The setting applies to both initial downloads and incremental updates. The same setting applies when running the `threat-data` command, but in that case only the `RL_CLOUD_UPDATE_CONCURRENCY` environment variable can be used. Increasing this value also increases the temporary additional disk space used during cloud threat data updates. - **Default**: `3` - **Possible Values**: From `1` to `32`. Values outside this range are rejected and replaced with the default. --- ### `--cloud-threat-data-scope` / `RL_CLOUD_THREAT_DATA_SCOPE` - **Description**: Selects the scope of the dataset included in the threat data. - **Default**: If no value is explicitly configured, the previously stored value is used. If that is also empty, `standard` is used. - **Possible Values**: `standard`, `non-limited` - **Notes**: - **`standard`**: A reduced dataset that excludes samples expected to be reliably detected through static analysis alone. Use this when performing file analysis through the [`/scan`](./usage.md#file-submissions) endpoint. Malicious threat data requires 20 GiB of disk space. - **`non-limited`**: A comprehensive dataset. Use this when FIE is used for hash lookups only, using [`/check-sample`](./usage.md#hash-lookups) endpoints. Malicious threat data requires 40 GiB of disk space. - Changing the scope requires a complete re-download of all malicious threat data segments. During the download, the application continues to operate with the previously available data. - For more information, see [Threat Database Variants](./usage.md#threat-database-variants). --- ### `--cloud-updates` / `RL_CLOUD_UPDATES` - **Description**: Enables or disables automatic updates for threat data. Cloud updates are automatically disabled when `--without-malicious-threat-data` is set to `true` and `--paranoid-mode` is set to `false`, as threat data is not used in that case. - **Default**: `true` - **Possible Values**: `true`, `false` --- ### `--http-address` / `RL_HTTP_ADDRESS` - **Description**: Defines the host and port for the HTTP server. - **Default**: :8000 - **Possible Values**: - Port only. Example: `:9000` - Host and port. Example: `127.0.0.1:8080` --- ### `--log-json` / `RL_LOG_JSON` - **Description**: Defines the log output format as either JSON or colored plain text. - **Default**: `true` - **Possible Values**: `true`, `false` --- ### `--log-level` / `RL_LOG_LEVEL` - **Description**: Sets the log level. - **Default**: `info` - **Possible Values**: `info`, `warn`, `error` --- ### `--max-decompression-factor` / `RL_MAX_DECOMPRESSION_FACTOR` - **Description:** Spectra Core has a set of mechanisms that protect the user from intentional or unintentional archive bombs, ranging from checks that prevent a file from making identical copies of itself during unpacking, to the maximum allowed decompression ratio for any given file. These protection measures enable the engine to terminate the archive decompression if the size of unpacked content exceeds a set quota. The maximum decompression ratio is calculated as ``` MaximumDecompressionFactor * (1000 / ln(1 + InputFileSize * pow(10, -5))) ``` where `InputFileSize` must be in bytes. To calculate the maximum decompressed file size, multiply this ratio by the `InputFileSize`. In practice, this means that the unpacking will stop once the size of all extracted content exceeds the theoretical maximum of the best performing compression algorithm. - **Default**: 1.0 - **Notes:** When a file exceeds the decompression ratio, the unpacking will stop and the partially unpacked content will be sent for analysis. If set to a negative value, a warning is printed, and the value defaults to 1.0. Setting this to 0 disables decompression management, but this is strongly discouraged as it leaves the system vulnerable to resource exhaustion attacks. --- ### `--max-upload-file-size` / `RL_MAX_UPLOAD_FILE_SIZE` - **Description**: Maximum file size (in MiB) the application will accept. - **Default**: 100 - **Minimum**: 1 - **Maximum**: 10240 --- ### `--number-of-regular-cores` / `RL_NUMBER_OF_REGULAR_CORES` - **Description**: Configures how many Spectra Core instances are allocated to handle files up to the size threshold (`--large-file-threshold`). - **Default**: 4 - **Possible Values**: 1-100 --- ### `--number-of-large-cores` / `RL_NUMBER_OF_LARGE_CORES` - **Description**: Configures how many Spectra Core instances are reserved for files larger than the size threshold (`--large-file-threshold`). - **Default**: 2 - **Possible Values**: 0-100 - **Notes**: If set to 0, no instances are reserved for large files, and all files are processed by the pool of "regular" instances. --- ### `--large-file-threshold` / `RL_LARGE_FILE_THRESHOLD` - **Description**: File size threshold (in MiB) that determines when a file is routed to the reserved large-file instances. - **Default**: 10 - **Possible Values**: 0-10240 - **Routing rules**: - Files **larger than** the threshold go to the large-file instances. - Files **equal to or smaller than** the threshold stay in the regular pool. - **Notes**: - When set to 0, size-based routing is disabled and all files are distributed across available instances. In this case, the system routes files to the instance with the fewest active analyses, rather than using file size. - File size is only an approximation of processing cost. Real resource usage depends on file complexity (number of unpacked children, nesting depth). Choosing an optimal threshold and timeout may require experimentation based on your workload. --- ### `--paranoid-mode` / `RL_PARANOID_MODE` - **Description**: Enables an additional classification for suspicious files, allowing them to be flagged as `suspicious` instead of `OK`. With this option, the possible response classifications are `OK`, `malicious` (if malicious threat data is not disabled), and `suspicious`. - **Default**: `false` - **Possible Values**: `true`, `false` - **Notes**: - Requires an additional 1 GiB of cloud threat data for suspicious classification. - When malicious or suspicious threat data is enabled, goodware classification is automatically enabled as well and requires 64 MiB of threat data. Goodware classification cannot be directly enabled or disabled. --- ### `--processing-unavailable-at-memory-percent` / `RL_PROCESSING_UNAVAILABLE_AT_MEMORY_PERCENT` - **Description**: Defines the memory usage threshold (in percentage) at which the application will reject new file uploads and return an error on the `/readyz` endpoint. This helps prevent overloading the system when memory usage is high. For example, to reject uploads once memory usage reaches 80%, use: `--processing-unavailable-at-memory-percent=80`. - **Default**: 0 (disabled) - **Possible Values**: 0–100 - **Notes**: The threshold is based on `cgroup v2` memory usage within the container. If your system doesn't support `cgroup v2`, you can disable this feature by setting the parameter to `0`. --- ### `--with-threat-details` / `RL_WITH_THREAT_DETAILS` - **Description**: Determines whether detailed threat information is included in the JSON HTTP response for malware classification. - **Default**: `false` - **Possible Values**: `true`, `false` - **Notes**: Slows down the response as it contacts the cloud API by submitting the file hash to Spectra Intelligence. If no additional threat information is available, the `threat_details` property won't be present. --- ### `--unpacking-depth` / `RL_UNPACKING_DEPTH` - **Description**: The maximum number of file layers to unpack when performing static analysis. - **Default:** `17` - **Possible values**: From `0` (unlimited) to MaxInt32. --- ### `--timeout` / `RL_TIMEOUT` - **Description**: Configures the timeout limit for file analysis, in seconds. The countdown starts when a Spectra Core instance begins processing a file. - **Default**: 0 (unlimited) - **Examples**: `--timeout=30s`, `--timeout=5m`, `--timeout=1h` - **Notes**: - When the timeout is reached, the Spectra Core instance is terminated and restarted. - If the instance was processing multiple files, all analyses are aborted. - Logs contain information about which files were impacted. - Before restart, the instance cleans up its temporary files. Restart time depends on the number of files and disk performance, but typically takes a few seconds. - Because restart takes time, very short timeout values are not recommended. --- ### `--without-malicious-threat-data` / `RL_WITHOUT_MALICIOUS_THREAT_DATA` - **Description**: Allows the application to run without downloading malicious threat data. When enabled, malicious threat data updates are disabled. If `--paranoid-mode` is also enabled, suspicious threat data will still be downloaded. When both malicious and suspicious threat data are disabled, files are classified based purely on static analysis. - **Default**: `false` - **Possible Values**: `true`, `false` - **Notes**: When malicious or suspicious threat data is enabled, goodware classification is automatically enabled as well and requires 64 MiB of threat data. Goodware classification cannot be directly enabled or disabled. --- **Example - Running with proxy and additional settings**--- ## Deploying File Inspection Engine: Requirements & Architecture ## Hardware Requirements To handle files of up to 2 GB, we recommend the following: - **Memory (RAM)**: - Provision **at least 32 GB of RAM**. File processing may require up to 8 times the file size in RAM, especially to accommodate large file handling and concurrent requests. - **Disk Size**: - Allocate **at least 100 GB of disk space** to support scanning of larger files and threat intelligence database storage requirements. - Note that during cloud threat data updates, additional disk space is temporarily used, proportional to the `--cloud-update-concurrency` setting. ### Production Deployment A single deployment can handle files of all sizes using multiple Spectra Core instances. - The total number of instances is configured with `--number-of-regular-cores` and `--number-of-large-cores`. - All instances are identical. The "large" group is simply a reserved subset of instances that only process files above the configured threshold (`--large-file-threshold`). - If no large instances are configured (set to 0), all files are processed by the regular pool. Each Spectra Core instance consumes memory even when idle (approximately 1.4 GB per instance), so factor this into your capacity planning. This approach improves CPU utilization and throughput while keeping the deployment architecture simple. --- ## Docker Deployment — File Inspection Engine ## Docker image The File Inspection Engine Docker image can be obtained from the ReversingLabs container registry. ### Pulling the Docker image To pull the Docker image from the ReversingLabs container registry: 1. **Log in to the Docker Registry** Log in using your cloud username and password: ```bash docker login registry.reversinglabs.com ``` 2. **Pull the Docker image** Pull the `file-inspection-engine` Docker image with the specified tag:## Running the application The File Inspection Engine (FIE) reads its license from an environment variable called `RL_LICENSE`. This license, provided by ReversingLabs, must be passed to the application at startup. To start the application, use the following commands.In this example, the container runs on the host network, so no port mapping is needed. **If you're not using the host network, you'll need to map the container's port to the host.** The HTTP server uses port 8000 by default, but you can change it: - To map the port to a different host port:- To change the HTTP port used by the container:## Storage and mounting considerations FIE uses two directories inside the container for storage: 1. `/rl/threat-data`, which it uses to assign file classifications. 2. `/rl/tmp`, which it uses to store file uploads, unpacked files, and file analysis reports. The `/rl/threat-data` directory contains roughly 20 GiB for malicious data, or 40 GiB with the [non-limited scope](../configuration.md#--cloud-threat-data-scope--rl_cloud_threat_data_scope), and 1 GiB for suspicious data, and additional space is needed during updates, as files are downloaded fully before replacement. Threat data synchronization starts shortly after the application is up and running and continues at regular intervals, configurable via the `--cloud-update-interval` parameter. Initial synchronization involves larger files, while subsequent updates use incremental changes (typically < 100 KB per segment). Data is divided into 256 segments per classification, and each segment may require multiple updates, which can increase the total download size to several megabytes, especially with less frequent updates. This means that a container started "bare" - without any threat data mounted upon start - will first need to pull in around 20 GiB of data, every time it is started. This radically decreases the performance of FIE, so **mounting an external volume is essential**, for example:This allows reusing threat data between containers, for example by transferring it to an [air-gapped instance](#air-gapped-manual-threat-data-synchronization). Mounting an external volume also means that you avoid the [performance costs](https://docs.docker.com/engine/storage/drivers/#copying-makes-containers-efficient) associated with writing to disk inside the container. **Warning: Reusing threat data must be done in **read-only** mode.** Individual FIE containers will, by default, continuously monitor and update their `/rl/threat-data` directory. Reusing threat data between several containers can lead to an issue with how containers interact with that directory. Therefore, make sure that containers which reuse the same source of threat data **do not write to it**. This can be accomplished by turning off [cloud updates](../configuration.md#--cloud-updates--rl_cloud_updates) for all containers which reuse the same data. **Note**: Do not reuse threat data even when only one container is writing. Even in this case, the read-only containers could potentially use old data *while it is being updated*. Since containers are only aware of their own threat data updates, they cannot detect another container being in the middle of an update. ### Selecting the mount type The two main factors to consider when choosing a mount type are **persistence** and **speed**. You want the `/rl/threat-data` directory to be persistent and have good read speed (as that's where the application will look when classifying files), and you want good write speed for `/rl/tmp`. If you're working directly with the threat data (as described in the [air-gapped instance](#air-gapped-manual-threat-data-synchronization) section), select a regular [bind mount](https://docs.docker.com/engine/storage/bind-mounts/). This allows you to freely interact with the downloaded data from the host system. You could also select a [Docker volume](https://docs.docker.com/engine/storage/volumes/#when-to-use-volumes) if you need a persistent source of data, but do not intend to directly interact with it. For `/rl/tmp`, persistence is not important, but write speed is. A possible choice here is [tmpfs mounts](https://docs.docker.com/engine/storage/tmpfs/). This also allows the highest throughput, as the underlying static analysis engine performs a lot of disk writes, and `tmpfs` mounts are RAM-only - which means that the write speeds will be faster. Note, however, that this requires allocating more RAM than e.g. using a bind mount. ## Manual threat data synchronization The File Inspection engine retrieves updates automatically. If you want to pre-download threat data so your customers can start using it immediately, or if you prefer to manually sync the data, use the `threat-data` command included in the image. This command is also used to [download threat data in air-gapped environments](#air-gapped-manual-threat-data-synchronization). If manual threat data updates occur less than once per week, incremental updates may take longer than a full database download. Performance depends on system resources, network bandwidth, and the deployment environment. Incremental updates are recommended by default, but if they are slow, consider these factors and opt for a full download if necessary. ### Supported Options The `threat-data` command supports the following options in addition to username and password: - `RL_CLOUD_THREAT_DATA_SCOPE` Select the threat data scope (`standard` or `non-limited`). - `RL_PARANOID_MODE` Download data collection for suspicious files. - `RL_PROXY_ADDRESS` Specify a proxy server address if you need to connect to the cloud via a proxy. - `RL_RETRY_COUNT` The number of retries if a segment fails to download during update. - `RL_CLOUD_UPDATE_CONCURRENCY` The number of concurrent network connections used during cloud threat data updates (1 to 32, default 3). - `RL_LOG_JSON` Defines the log output format as either JSON or colored plain text. ### Sync Command To manually sync the threat data, use the `sync` sub-command, which requires specifying the threat data directory: ```bash ./threat-data sync /threat/data/dir ``` To execute this via Docker, run:If you need to treat suspicious files as malicious, make sure to set the `RL_PARANOID_MODE` option to `true` in the command. **Important**: - The `threat-data` command only supports configuration via environment variables. - We recommend pre-downloading the threat data once and including it in your distribution for multiple users, as a full threat data download is more resource-intensive compared to incremental updates. - **Do not** run the `threat-data` command concurrently with the application if both are accessing the same directory. - **Always use the `threat-data` binary from the same FIE version** as your running container. Older binaries are not compatible with newer threat databases. Extract the binary from the container if needed: `docker cp :/rl/app/threat-data ./threat-data` ## Air-gapped manual threat data synchronization For air-gapped environments, follow the process below to synchronize threat data. First, download the threat data on a machine with internet access, then transfer the data to the air-gapped instance. 1. Start a File Inspection Engine (FIE) instance on a machine with internet access. Once the data sync is complete, stop the FIE instance that was used for downloading, and then proceed to step 2. Alternatively, run the following command to manually sync the threat data:`/external/dir` represents the path on the host system where the threat data is stored. If the directory contains older threat data, it will be incrementally updated. **Note: If using paranoid mode, set the environment variable `RL_PARANOID_MODE=true`.** Upon successful synchronization, the log should show `Threat data fully updated`. In case of errors, rerun the command to retry. Proceed to step 2. 2. Stop a production FIE instance (or create a new one) in the air-gapped environment. 3. Copy the threat data from `/external/dir` on the internet-connected machine to the corresponding threat data directory used by the air-gapped FIE instance. Ensure that the transferred data is placed in the directory where the application would normally download it if it were online. For further assistance, contact [ReversingLabs Support](mailto:support@reversinglabs.com). 4. Restart or deploy the air-gapped FIE instance with the updated threat intelligence data. --- ## Kubernetes Deployment — File Inspection Engine ## Introduction A typical File Inspection Engine (FIE) installation is performed on Kubernetes using Helm. Throughout this document, we'll be using Google Kubernetes Engine (GKE) as an example. For managed Kubernetes solutions, you may also need to use vendor-specific tools to interact with the cluster. In our example, this will be `gcloud`. To install `gcloud`, follow [these steps](https://cloud.google.com/sdk/docs/install). ------ ## Deploying FIE Helm Chart to GKE Here is an overview of deploying the FIE Helm chart to a GKE cluster: ### Prerequisites - A GKE cluster is available. - `kubectl` is configured to work with your cluster. - Helm is installed. ------ ### Example: Configuring `kubectl` for a Specific Cluster 1. **List Available GKE Clusters:** ```bash gcloud container clusters list ``` **Example Output:** ```bash NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS gke-autopilot-ado-dev us-east4 1.28.8-gke.1095000 35.199.55.139 e2-small 1.28.8-gke.1095000 2 RUNNING ``` 2. **Get Cluster Credentials:** Run the following command to fetch cluster endpoint and authentication data: ``` gcloud container clusters get-credentials gke-autopilot-ado-dev --region us-east4 ``` **Output:** ```bash Fetching cluster endpoint and auth data. kubeconfig entry generated for gke-autopilot-ado-dev. ``` ------ The FIE Helm chart requires valid Spectra Intelligence credentials, which will be provided by ReversingLabs. ## Install FIE Using Helm The examples provided use a placeholder account (`u/example/fie`). Be sure to replace this with your actual credentials wherever applicable. ### Customize the Installation with a Values File For the full list of available values, see the [example values.yaml](./Examples/values.md) file. You can modify values such as ingress or storage class according to your needs. This example exposes the application internally using a load balancer service. ### Set the password and install the Helm Chart 1. Store the password in a variable: ```bash read -rs SPECTRA_INTELLIGENCE_PASSWORD ``` 2. Log in to the ReversingLabs container registry: ```bash echo "${SPECTRA_INTELLIGENCE_PASSWORD}" | helm registry login -u "u/example/fie" --password-stdin registry.reversinglabs.com ``` 3. Install the Helm chart: ```bash $ helm install fie oci://registry.reversinglabs.com/fie/charts/fie \ --create-namespace --namespace fie \ --set settings.cloudPassword="${SPECTRA_INTELLIGENCE_PASSWORD}" \ --values values-deploy-example-gcp.yaml \ --set-file licenseFileContent=rl-license.enc ``` **Expected Output:** ```bash Pulled: registry.reversinglabs.com/fie/charts/fie:0.2.1 Digest: sha256:61ed7f0761912cc5052ceac1d71654f3c1f89f543df0ab6ae3d199070ab02084 NAME: fie LAST DEPLOYED: Tue May 28 11:20:34 2024 NAMESPACE: fie STATUS: deployed REVISION: 1 TEST SUITE: None ``` ### CPU Requests The `/status` endpoint shows both counts (`available_*`) and percentages (`percentage_*`) for Spectra Core instances. The percentages are based on the value of the `RL_CPU_REQUEST` environment variable. - **Helm deployments**: Helm automatically maps `resources.requests.cpu` into this variable. No extra configuration is needed. - **Non-Helm deployments**: You must set `RL_CPU_REQUEST` yourself. This does not affect how many Spectra Core instances are created or how Kubernetes schedules the pod. It only affects how percentages are reported in `/status`. You can provide this value in three ways: 1. **Command-line flag** ```yaml args: ["--cpu-request=8"] ``` 2. **Environment variable** ```yaml env: - name: RL_CPU_REQUEST value: "8" ``` 3. **Kubernetes Downward API** ```yaml resources: requests: cpu: "8" env: - name: RL_CPU_REQUEST valueFrom: resourceFieldRef: containerName: fie resource: requests.cpu ``` In this last example, the Pod requests 8 CPUs, and Kubernetes injects that value into the container as `RL_CPU_REQUEST`. FIE then uses it only for calculating the `percentage_*` fields in `/status`. ------ ### Get the application URL After deployment, obtain the application URL and port by running one of the following commands: 1. **LoadBalancer IP:** > **Note:** It may take a few minutes for the LoadBalancer IP to be available. You can watch the status by running `kubectl get --namespace fie-ag svc -w fie`. ```bash export SERVICE_IP=$(kubectl get svc --namespace fie fie \ --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}") echo http://$SERVICE_IP:8000 ``` 2. **Verify the deployment** ```bash kubectl -n fie get svc/fie ``` **Expected output** ```bash NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE fie LoadBalancer 34.118.234.76 10.128.0.24 8000:32132/TCP 32m ``` --- Once you have the service IP and port, you can send a test query to the liveness/readiness endpoints: ```bash curl -v http://10.128.0.24:8000/livez curl -v http://10.128.0.24:8000/readyz ``` **Note: Check the [API Reference](../usage.md#check-application-liveness) for more information on these two endpoints.** --- Alternatively, submit a file for analysis. This only works after the [threat data is fully downloaded](#monitoring-the-threat-data-download): ```bash curl -sS -X POST --upload-file eicar.com http://10.128.0.24:8000/scan | jq ``` **Expected output** ```json { "classification": "malicious", "message": "" } ``` ------ ### Monitoring the Threat Data Download After deployment, the FIE application will begin downloading threat data. This process can take between 30 and 90 minutes, depending on your network speed. You can monitor the download process by running: ```bash kubectl -n fie logs -f deploy/fie ``` Look for the following message, which indicates that the download process completed successfully: ```json {"level":"info","component":"threatdata.UpdateManager","time":"2024-09-18T22:32:58.346353125Z","message":"Cloud update run finished"} ``` ## Appendix: FIE Helm Chart ### Download the Helm Chart To download the Helm Chart, run the following commands: ```bash helm registry login -u "$RL_CLOUD_USERNAME" -p "$RL_CLOUD_PASSWORD" registry.reversinglabs.com helm pull oci://registry.reversinglabs.com/fie/charts/fie ``` --- ## File Inspection Engine Air-Gapped Deployment If the network topology of a Kubernetes cluster prevents access to the ReversingLabs registry and APIs, several objects must be manually transferred and uploaded to the cluster. It is crucial to have Kubernetes API access available since `kubectl` will be used throughout this process. **Steps:** - **Download the threat data manually**: Use a FIE instance with internet access to download threat data. - **Deploy FIE in production**: Deploy the production FIE application with cloud updates disabled - **Transfer threat data**: Copy the downloaded threat data to the air-gapped FIE instance. To complete this process, you will need: - The [FIE Helm Chart](./kubernetes.md#appendix-fie-helm-chart) - The FIE container image [pulled from the ReversingLabs registry](./docker.md#pulling-the-docker-image), which must be made available to the Kubernetes cluster via a client-provided registry. - A valid license (provided by ReversingLabs). ### Manually download the threat data The detailed process for downloading threat data is available [here](./docker.md#manual-threat-data-synchronization). ### Deploy the FIE application After the threat data downloads, deploy FIE using the [Helm chart](./kubernetes.md#appendix-fie-helm-chart). #### Making the container image available to Kubernetes To make the FIE container image available to the Kubernetes cluster, you need to [pull it from the ReversingLabs registry](./docker.md#pulling-the-docker-image) and push it to your own registry. Follow these steps: 1. **Load the image**2. **Tag the image**3. **Push the image to your registry****Note: Podman is used in this example, but the syntax should be similar if using Docker.** #### Installing FIE Using Helm Prepare a custom values file to configure the deployment using the [FIE Helm Chart](./kubernetes.md#install-fie-using-helm). Consult with your Kubernetes administrator to decide how to expose the FIE service (e.g., LoadBalancer, Ingress). For the full list of available values, see the [example values.yaml](./Examples/values.md) file. In this example, we use a LoadBalancer service to expose FIE, and we override the default image repository and tag with the settings from the previous step. **Example Configuration (configuration.yaml):**Once you have prepared the values file, you can proceed to install the Helm chart. The Helm chart can be pushed to a chart repository, an OCI repository, or used directly as shown below: **Example Helm installation command** ```bash helm install fie ./fie-0.2.1.tgz --create-namespace --namespace fie-gapped \ --set settings.cloudPassword="$RL_CLOUD_PASSWORD" \ --values configuration.yaml --set-file licenseFileContent=RL-license.enc ``` ```yaml NAME: fie LAST DEPLOYED: Mon Aug 26 11:57:56 2024 NAMESPACE: fie-ag STATUS: deployed REVISION: 1 TEST SUITE: None ``` **Note: CPU Requests: The `RL_CPU_REQUEST` environment variable exposes the number of CPU requests configured in Kubernetes (`resources.requests.cpu`) to the FIE container.** FIE uses this value to report instance usage in `/status` as a percentage relative to the allotted CPU. - **If deploying with our Helm chart**: the chart template maps `resources.requests.cpu` into this variable (see the [Kubernetes deployment guide](./kubernetes.md#cpu-requests)). - **If deploying without Helm**: define `RL_CPU_REQUEST` in the container's environment section of your Pod or Deployment manifest. ### Copy the threat data There are multiple ways to transfer the threat data to the air-gapped environment. Below is one example workflow: 1. **Download the tar package** We will store the threat data into a .tar file. This requires the `tar` package to be installed in the FIE pod. Since this is an air-gapped environment, the `tar` package must be downloaded externally and then transferred to the pod: ```bash curl -O https://cdn-ubi.redhat.com/content/public/ubi/dist/ubi8/8/x86_64/baseos/os/Packages/t/tar-1.30-9.el8.x86_64.rpm ``` This is an example command, make sure to check that you're downloading the latest available version. 2. **Upload the tar package to FIE and install it** ```bash cat tar-1.30-9.el8.x86_64.rpm | kubectl -n fie-gapped exec -it deploy/fie -- cp /dev/stdin /tar-1.30-9.el8.x86_64.rpm ``` ```bash kubectl -n fie-gapped exec -it deploy/fie -- rpm -ihv /tar-1.30-9.el8.x86_64.rpm ``` 3. **Store and transfer threat data** Once `tar` installs, threat data can be stored into a .tar archive and moved over to the pod: ```bash $ cd /external/dir $ tar cvf - * | kubectl -n fie-gapped exec -i deploy/fie -- tar xf - -C /rl/threat-data --no-same-owner ``` 4. **Restart the pod** After everything is installed and copied over, restart the pod: ```bash kubectl -n fie-gapped rollout restart deploy/fie ``` Contact [ReversingLabs Support](mailto:support@reversinglabs.com) for more information and guidance. --- ### Get the application URL To confirm that the File Inspection Engine is up and running, retrieve the application URL and perform a test file submission. You can follow the steps provided in the [Kubernetes Deployment guide](./kubernetes.md#get-the-application-url). --- ## Example values.yaml file ```yaml # Default values for fie. # This is a YAML-formatted file. # Declare variables to be passed into your templates. image: repository: registry.reversinglabs.com/fie/file-inspection-engine pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. tag: "" imagePullSecrets: {} # value not used for RL registry licenseFileContent: "" # FIE license received from ReversingLabs nameOverride: "" fullnameOverride: "" podAnnotations: {} podSecurityContext: {} securityContext: {} storage: existingPvcName: "" # set name to use existing pvc size: 32Gi # min. 22Gi atm. className: gp2 rlTmpInRam: true tmpfsSize: 20Gi service: annotations: {} # To set an internal load balancer, refer to your cloud service provider documentation. type: ClusterIP port: 8000 ingress: enabled: false className: "alb" annotations: {} # There are different features supported by various Ingress controllers. Please refer to # documentation on your platform specific Ingress controller to configure it in your environment. hosts: - host: fie.local.lan paths: - path: / pathType: Prefix tls: [] # - secretName: fie-tls # hosts: # - fie.local.lan resources: requests: cpu: 8 memory: 32Gi ephemeral-storage: 100Gi nodeSelector: {} tolerations: [] affinity: {} settings: # This option is available only for alpha6 and above. Possible values: enabled / disabled / force addFileType: "disabled" # Cloud account password, used only for default registry auth cloudPassword: "" # Scope of the dataset included in the threat data. Possible values: standard, non-limited. # If empty, the previously stored value is used; if that is also empty, standard is used. cloudThreatDataScope: "" # The number of concurrent network connections used during cloud threat data updates (1 to 32). cloudUpdateConcurrency: 3 # Desired frequency of cloud threat data updates, between 1 minute and 24 hours. Use m for minutes # and h for hours, e.g. 45m or 6h. cloudUpdateInterval: "5m0s" # Automatic updates of cloud threat data cloudUpdates: true # Cloud account username, used only for default registry auth cloudUsername: "" # Maximum concurrent requests performing file analysis, across all HTTP endpoints. Allowed values are from 0 (unlimited) to 100 concurrencyLimit: 20 # The address and port on which the HTTP server will listen. httpAddress: ":8000" # Files larger than this will be analyzed by large Spectra Core instances (0 to 10240 MiB). When 0, there is no distinction between instances. largeFileThreshold: 10 # Set the max decompression factor to limit resource usage during decompression, with 0 meaning no limit. maxDecompressionFactor: 1.0 # The value needs to be between 1 and 10240 MiB. Uploads larger than this will be rejected. maxUploadFileSize: 2048 # The number of Spectra Core instances that will process large files (0 to 100) numberOfLargeCores: 2 # The number of Spectra Core instances that will process regular files (1 to 100) numberOfRegularCores: 4 # Whether suspicious samples should be classified as malicious paranoidMode: false # cgroup v2 memory use percentage that triggers rejection of new file uploads. Allowed values are from 0 (disabled) to 100. processingUnavailableAtMemoryPercent: 0 # The host and port of a proxy for outgoing HTTP connections. It can optionally include one of the # following three schemes: http, https, socks5. Example: socks5://host:port proxyAddress: "" # Maximum analysis time, for example 10s (seconds) or 1m (minute). The default is 0, which means unlimited. timeout: "0" # The maximum number of file layers to unpack when performing static analysis. Valid values # are from 0 (unlimited) to MaxInt32. Default 17 unpackingDepth: 17 # Includes detailed information about malicious samples in the HTTP response withThreatDetails: false # Do not look up samples in the malicious threat data. The files don't need to be present locally either. withoutMaliciousThreatData: false ``` --- ## File Inspection Engine API Reference ## General ### Starting the File Inspection Engine The File Inspection Engine starts the main application process separately from its analysis instances. The application will start successfully in most cases, except for fatal configuration errors. - The license is checked during startup. - Threat data must be downloaded before analysis can begin. - Analysis instances initialize independently. The application is considered ready when at least one instance is ready, which can be seen in the logs or through the `/status` endpoint. To verify overall readiness, use the readiness endpoint: ```bash curl http://:/readyz ``` If the application is ready, this returns `200 OK`. The `/readyz` endpoint is the recommended way to confirm readiness, because it checks not only instance availability but also license validity, threat data availability, and the concurrency limit. **Note: When starting for the first time, the application needs to download threat data. This process may take some time, and the application will only become fully usable once the threat data download is complete, regardless of the messages displayed.** If the static analysis engine fails to initialize or another fatal configuration error occurs, the application will exit. For all other errors, logs will be generated, and the application will continue to run. ### Possible Response Status Codes If present, the `errors` and `message` fields may contain soft errors (e.g., failing to get detailed threat information from the cloud), but are often empty. Hard errors will be returned as HTTP status `500 Internal Server Error`. |Code | Description | Message| |-----|--------------|------------ |200 | The request has succeeded. | N/A| |400 | File size error. | `{"error": "Maximum upload file size in bytes is {configured_value}" }` | |429 | Concurrency limit reached. | `{"error":"The concurrency limit has been reached"}` | |429 | High processing load | `{"error":"Analysis not accepted due to high processing load"}` | |524 | A timeout occurred. | `{"error": "The analysis could not be completed within the configured maximum analysis time"}`| ## Classification and Threat Data Configuration The File Inspection Engine's classification behavior is controlled by two key configuration options that work together: `--without-malicious-threat-data` and `--paranoid-mode`. When `--without-malicious-threat-data` is enabled, the engine skips downloading malicious threat data and relies on static analysis for classification. The `--paranoid-mode` option adds a third classification level (`suspicious`) and requires additional threat data. When both `--without-malicious-threat-data=true` and `--paranoid-mode=false`, cloud threat data updates are automatically disabled since no threat data is needed. However, if `--paranoid-mode=true`, suspicious threat data will still be downloaded even when malicious data is disabled. Related configuration options: - [`--without-malicious-threat-data`](./configuration.md#--without-malicious-threat-data--rl_without_malicious_threat_data) - [`--paranoid-mode`](./configuration.md#--paranoid-mode--rl_paranoid_mode) ### Threat Database Variants The File Inspection Engine supports the following threat database variants: **standard** and **non-limited**. - **Standard**: A reduced dataset that excludes samples expected to be reliably detected through static analysis alone. Use this when performing file analysis through the [`/scan`](#file-submissions) endpoint. Malicious threat data requires 20 GiB of disk space. - **Non-limited**: A comprehensive dataset. Use this when FIE is used for hash lookups only, using [`/check-sample`](#hash-lookups) endpoints. Malicious threat data requires 40 GiB of disk space. **Note: Switching between variants requires a complete re-download of all malicious threat data segments. The application continues to operate with the previously available data during the download.** Related configuration option: - [`--cloud-threat-data-scope`](./configuration.md#--cloud-threat-data-scope--rl_cloud_threat_data_scope) ### Classification Overrides The File Inspection Engine supports the following types of classification overrides: - **Goodware overrides**: Part of the goodware threat data classification. These are files marked as goodware by ReversingLabs analysts and are automatically downloaded when cloud updates are enabled. The goodware classification requires 64 MiB of threat data. Analyst overrides are always goodware classifications. - **User overrides**: Custom classifications by other users within the same organization (indicated by the middle segment of the username - u/**company**/user). Users can change a file's classification to malicious, suspicious, or goodware. They are automatically downloaded when cloud updates are enabled, their count is logged at application startup. **Creating User Overrides** User overrides can be created in the following ways: 1. **Spectra Analyze**: Override classifications **in [Spectra Intelligence](/SpectraIntelligence/)** for specific files through the [Spectra Analyze user interface](/SpectraAnalyze/getting-started/#administering-classification-overrides). 2. **Spectra Intelligence API**: Use the [TCA-0102 File reputation override](/SpectraIntelligence/API/FileThreatIntel/tca-0102) service to programmatically create user overrides. Both types of overrides apply only to container files, i.e. to the top-level file being scanned, and not to unpacked children within archives. For example, a file with a user override that is in a ZIP archive will not have the override applied. ## File Submissions ``` POST /scan ``` To scan a file, make a POST request to `http://:/scan`, with the file contents as the raw request body. - You don't need to set the `Content-Type` header. If set, it will be included alongside every log message related to that submission. - It is recommended to set the `Content-Length` header to prevent files larger than the maximum upload size from partially uploading before getting rejected. - Optionally, you can provide an `external_id` query string parameter that will also be included alongside log messages related to the submission. This parameter can contain any value meaningful to the client application, such as a file name, database ID, or other identifying information. ### Examples using `curl` **Example request:** ```bash curl -X POST --upload-file example.docx http://:/scan ``` **Example response:** ```json5 { "classification": "OK", "message": "", "errors": [] } ``` **Classification**: The `classification` string will be either `"OK"` or `"malicious"`. If [paranoid mode](./configuration.md#--paranoid-mode--rl_paranoid_mode) is turned on, the classification could also be `"suspicious"`. If `with-threat-details` and `add-file-type` options are enabled, the response may look like: ```json { "classification": "malicious", "message": "", "errors": [], "threat_details": { "platform": "Script", "type": "Trojan", "threat_name": "Script-JS.Trojan.Redirector" }, "file_type": "Text" } ``` **Logging:** The following example provides `Content-Type` and a custom external ID in the request, both of which can be visible in application logs: ```bash curl -X POST -H 'Content-Type: application/x-tar' --upload-file archive.tar 'http://localhost:8000/scan?external_id=my%20external%20id' ``` Log example: This request would create the following log entry, including the external ID: ```json { "level": "info", "process": "fie", "request_id": "2fd7364b-50c5-4128-ae08-35a819dda62f", "external_id": "my external id", "content_type": "application/x-tar", "component": "http.api", "request_path": "/scan", "content_length": 244244480, "active_concurrency": "1/20", "time": "2025-09-24T15:25:06.350178812+02:00", "message": "Upload started" } ``` ### Error handling If there are any errors, they will be returned in the `message` field (deprecated), as well as the `errors` field. The `message` field will contain the same errors as the `errors` array, only it will be a semicolon-concatenated string. For example: ```json5 { "message": "error one; error two; error three", "errors": ["error one", "error two", "error three"] } ``` In some cases, certain errors are expected, and are converted to additional properties inside `analysis_information`. For example, if a file hits the [decompression factor limit](./configuration.md#--max-decompression-factor--rl_max_decompression_factor), this error will be logged in `errors` and `message`, but also present in `analysis_information.partial_unpacking`. ```json5 { "errors": ["Exceeds decompression ratio."], "message": "Exceeds decompression ratio.", "analysis_information": { "partial_unpacking": true } } ``` ## Hash Lookups Use the following endpoints to check sample classification by sample or hash without triggering static analysis. **Warning: Hash lookups rely entirely on the threat database and do not perform static analysis. If hash lookups are your primary or only classification method, use the [non-limited threat database variant](#threat-database-variants).** ### Compute hash and perform lookup ``` POST /check-sample/upload ``` To use a file to compute its hash and check its classification, make a POST request with the file sample as the raw request body. **Note: Even though the sample is not sent for static analysis, the configured file size limit still applies.** #### Examples using `curl` **Example request:** ```bash curl -X POST --upload-file sample 'http://localhost:8000/check-sample/upload' ``` **Example response:** ```json5 { "classification": "malicious" } ``` **Classification**: The `classification` string will be either `"OK"` or `"malicious"`. If [paranoid mode](./configuration.md#--paranoid-mode--rl_paranoid_mode) is turned on, the classification could also be `"suspicious"`. If the `with-threat-details` option is enabled, the response may look like: ```json { "classification": "malicious", "threat_details": { "platform": "Win32", "type": "Malware", "threat_name": "Win32.Malware.Heuristic" } } ``` ### Provide hash and perform lookup ``` GET /check-sample/hash/{name}/{value} ``` To check a file's classification by providing its hash, make a GET request using the following path parameters: - `name`: Hash type; currently supports only `sha1` - `value`: SHA1 hash string #### Examples using `curl` **Example request:** ```bash curl -X GET 'http://localhost:8000/check-sample/hash/sha1/74577262dad60dc5bf35c692f23300c54c92cb53' ``` **Example response:** ```json5 { "classification": "malicious" } ``` **Classification**: The `classification` string will be either `"OK"` or `"malicious"`. If [paranoid mode](./configuration.md#--paranoid-mode--rl_paranoid_mode) is turned on, the classification could also be `"suspicious"`. If the `with-threat-details` option is enabled, the file hash will also be submitted to the cloud API to retrieve additional threat details. The response may look like: ```json { "classification": "malicious", "threat_details": { "platform": "Win32", "type": "Malware", "threat_name": "Win32.Malware.Heuristic" } } ``` ## Request Rejection When a `/scan` or `/check-sample` upload request reaches an FIE instance, the engine first performs a readiness check before accepting the file for processing. Depending on the setup, readiness checks can be approached in two ways: - Kubernetes Readiness: Point your container's readiness probe to `/readyz`. When a container fails its readiness check, Kubernetes marks the Pod as not ready, and Services automatically stop routing traffic to it. - External Load Balancer: Configure your load balancer's health check on `/readyz` so only ready instances receive traffic. These probes are helpful to keep traffic away from busy nodes, but they are optional. The engine also performs its own readiness check for each file upload. **Note: Depending on the delay between the readiness check and the file submission, it is possible that the application returns a ready state, but the file can still be rejected if the conditions change. Such files will have to be resubmitted.** The engine evaluates several conditions to determine whether a file can be accepted. Some conditions are influenced by system state, such as memory usage or processing load, while others, such as concurrency, are driven by the volume of incoming requests. Logging occurs when any of these conditions change, regardless of file submission activity. ### Memory Usage The system can **optionally track memory usage** and compare it to a configured threshold (`processing-unavailable-at-memory-percent`). When enabled, memory usage is calculated as a percentage of either: - the [memory limit defined for the container](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) (if set), or - the total available system memory (if no container limit is present). If this threshold is exceeded at the time of file submission, the application will reject the file until memory usage drops below the limit. **Note: If the temporary directory is configured as `tmpfs`, it will be counted toward memory usage.** Because memory usage depends on the complexity and unpacking behavior of previously submitted files, it may remain elevated even after no new uploads occur. The system logs when it enters and exits high-memory conditions independently of file submissions. If the threshold is not configured, memory usage is **not tracked or logged**, and file rejection based on memory use is disabled. ### Concurrency Limit The system enforces a limit on concurrent requests, defined by the `concurrency-limit` setting. If the number of active concurrent requests exceeds this limit, new submissions are temporarily rejected. The concurrency limit applies **globally** to all uploads, regardless of whether they are routed to the regular core pool or cores reserved for large files. Even if the concurrency limit is set to `0` (unlimited), the system still tracks the number of active concurrent requests. Concurrency is controlled by the number of active file submissions at any given moment. This value is directly influenced by the client's submission behavior, making it a more predictable limit compared to memory or processing-based conditions. ### Multiple Cores Requests are assigned to [Spectra Core](/General/AnalysisAndClassification/SpectraCoreAnalysis) instances. Each file is always processed by a single instance. - All instances are identical in capability. - You can configure a subset of instances to be reserved for files larger than the size threshold (`--large-file-threshold`). - Large-file instances process only **one file at a time**, regardless of the global concurrency limit. - If no large instances are configured, all files are handled by the same pool. The `/readyz` endpoint returns `200 OK` if at least one instance (regular or large-file) is available. For detailed availability per group, use the `/status` endpoint. ## Logging If a file upload is rejected due to memory or processing conditions, the application will return the HTTP status `429 Too Many Requests`. The application logs when it enters and exits high-load or high-memory states. These log entries are independent of file submission attempts, since resource usage is influenced by the complexity of previously submitted files, not just their size or frequency. ### Log Examples **Processing Load: High and Normal** When a Spectra Core instance becomes busy, logs indicate that it cannot accept new files: ```json { "level": "info", "process": "fie", "instance_id": "core-large-0.8qczl", "time": "2025-09-24T15:39:07.618375188+02:00", "message": "Instance is not ready" } { "level": "warn", "process": "core", "instance_id": "core-large-0.8qczl", "time": "2025-09-24T15:39:58.946518488+02:00", "message": "High processing load" } ``` When the load subsides and the instance can accept new files again, logs show a return to normal: ```json { "level": "info", "process": "core", "instance_id": "core-large-0.8qczl", "time": "2025-09-24T15:40:15.592637095+02:00", "message": "High processing load over" } { "level": "info", "process": "fie", "instance_id": "core-large-0.8qczl", "time": "2025-09-24T15:40:15.677907694+02:00", "message": "Instance is ready" } ``` - `Instance is not ready` – The instance cannot accept new files right now. The `/status` endpoint will show it as unavailable, and new submissions will be rejected until it becomes free again. - `High processing load` – The instance is busy. This is an additional signal but less important for deciding whether to submit new files. - `High processing load over` – The instance has returned to a normal state. - `Instance is ready` – The instance is available again, either because load subsided or after a restart. **Note: If an instance recovers due to a timeout and restart, the return-to-normal sequence is slightly different: only the `Instance is ready` message appears after the restart.** **Memory Usage: High and Normal** ```json { "level": "warn", "process": "fie", "component": "readiness.Controller", "time": "2025-09-24T13:55:38.184606708Z", "message": "Memory use is above the threshold of 90%" } { "level": "info", "process": "fie", "component": "readiness.Controller", "time": "2025-09-24T13:56:33.183810287Z", "message": "Memory use is below the threshold of 90%" } ``` ## Timeouts The engine enforces a configurable **timeout** for file analysis. If a file exceeds the configured timeout: - The Spectra Core instance handling the file is terminated and restarted. - Any other analyses running on that instance are aborted. - Logs will show that the analysis was aborted and timed out. - The instance will need to restart and become ready again before it can accept new files. This usually takes a few seconds, but the exact time depends on system load and disk speed. For this reason, very short timeout values are not recommended. ```json { "level": "warn", "process": "fie", "request_id": "3dc57796-2964-4249-bff3-c98ddef747ca", "component": "scanner", "sample_size": 86271956, "sample_sha1": "0aa2f850f0e87ef84743f518a10d17e3b03395d7", "sample_type": "application/x-unix-archive", "scan_duration_ms": 60000.502133, "analyzed_files": 0, "timeout": "1m0s", "time": "2025-09-23T17:35:57.299468984+02:00", "message": "Analysis aborted due to a timeout" } { "level": "warn", "process": "fie", "request_id": "3dc57796-2964-4249-bff3-c98ddef747ca", "component": "http.api", "request_path": "/scan", "content_length": 86271956, "scan_duration_ms": 60349.621584, "sample_sha1": "0aa2f850f0e87ef84743f518a10d17e3b03395d7", "time": "2025-09-23T17:35:57.307513847+02:00", "message": "Analysis has timed out" } ``` After a timeout, the affected instance restarts. Once recovery is complete, it logs that it is ready again: ```json { "level": "info", "process": "fie", "instance_id": "core-regular-0.pwcpx", "time": "2025-09-23T17:36:07.943715309+02:00", "message": "Instance is ready" } ``` **Note: The random suffix in the `instance_id` changes after a restart.** ### Check for Hard Timeout You can look at log messages to determine if your instance has experienced a hard timeout. For example: ``` {"level":"warn","process":"fie","component":"core.process","instance_id":"core-regular-0.dmssx","time":"2025-12-01T13:53:14.665857373+01:00","message":"Hard timeout"} ``` ## Check Application Liveness ```bash curl http://:/livez ``` Returns `200 OK` if the application process is running. Use for Kubernetes liveness probes. ## Check Application Readiness ```bash curl http://:/readyz ``` Returns `200 OK` only if: - The engine has fully initialized (including license validation) and is not too busy to process samples. - Current resource utilization is within configured limits (memory, concurrency) and the system is not too busy (Spectra Core, CPU/load) to process samples. - If not ready, returns a `4xx` or `5xx` status. ## Check Application Version / License / Configuration ``` GET /status ``` To check the File Inspection Engine version, license expiration date, threat data timestamp, and configuration, use the `/status` endpoint. The `config` section contains all the [configurable options](./configuration.md) and their current values. Values containing sensitive information are redacted. The `spectra_core` object describes how analysis instances are configured and available: - **percentage_of_regular_cores**: The number of regular instances expressed as a percentage of the total CPU requests. - If no large-file pool is configured (`--number-of-large-cores=0`), these instances handle all files. - Otherwise, they handle files up to the `--large-file-threshold`. - **percentage_of_large_cores**: The number of large-file instances expressed as a percentage of the total CPU requests. - **available_regular_cores / available_large_cores**: Current availability of instances in each pool (available / total). - **total_cpus**: The total number of CPUs used for calculating percentages. If `RL_CPU_REQUEST` is set, this value comes from that variable. Otherwise, it reflects the total CPUs detected on the node. **Note: The `percentage_*` fields compare instance counts to the CPU request value. They do not represent actual CPU allocation. Each instance may use more or fewer CPUs depending on workload and system limits.** The `threat_data` object describes the threat data used by the engine: - **enabled_classifications**: The classifications enabled in the threat data, based on the combination of the `--without-malicious-threat-data` and `--paranoid-mode` options. When malicious or suspicious threat data is enabled, goodware classification is also automatically enabled. - `--without-malicious-threat-data=false + --paranoid-mode=false` -> `["malicious", "goodware"]` - `--without-malicious-threat-data=false + --paranoid-mode=true` -> `["malicious", "suspicious", "goodware"]` - `--without-malicious-threat-data=true + --paranoid-mode=false` -> `[]` (no threat data is used, files are classified based on static analysis) - `--without-malicious-threat-data=true + --paranoid-mode=true` -> `["suspicious", "goodware"]` - **fp_probability**: The false positive probability for each classification. Not shown for disabled classifications. - **scope**: The threat data scope currently in use, `standard` or `non-limited`. See [Threat Database Variants](#threat-database-variants). **Example Response** ```bash curl http://:/status ``` ```json5 { "config": { "add_file_type": "disabled", "cloud_threat_data_scope": "", "cloud_update_concurrency": 3, "cloud_update_interval": "5m0s", "cloud_updates": true, "concurrency_limit": 20, "cpu_request": 8, "http_address": ":8000", "large_file_threshold": 10, "log_json": true, "log_level": "info", "max_decompression_factor": 1, "max_upload_file_size": 100, "number_of_large_cores": 2, "number_of_regular_cores": 4, "paranoid_mode": true, "processing_unavailable_at_memory_percent": 0, "proxy_address": "http://user:xxxxx@proxy.company.lan", "timeout": "0s", "unpacking_depth": 17, "with_threat_details": false, "without_malicious_threat_data": false }, "license": { "valid_until": "2026-03-01" }, "spectra_core": { "available_large_cores": "100% (2/2)", "available_regular_cores": "100% (4/4)", "percentage_of_large_cores": "25% (2)", "percentage_of_regular_cores": "50% (4)", "total_cpus": 8 }, "threat_data": { "enabled_classifications": [ "malicious", "suspicious", "goodware" ], "fp_probability": { "goodware": "1 in 4.221353e+87 samples", "malicious": "1 in 1.154405e+12 samples", "suspicious": "1 in 2.931565e+12 samples" }, "scope": "standard" }, "version": { "application": "3.5.0", "threat_data": "2026-05-05T13:06:18Z" } } ``` --- ## File Inspection Engine Troubleshooting Guide # Troubleshooting This guide covers common issues with [File Inspection Engine](./index.md) (FIE) and the steps to resolve them. --- ## Container exits immediately on startup **Symptom** The FIE container starts and exits within a few seconds. `docker ps` shows it in an `Exited` state. The container never becomes ready to accept requests. **Cause** - A fatal configuration error occurred during initialization, such as a missing or malformed `RL_LICENSE` environment variable. - A required CLI flag has an invalid value (for example, an invalid duration format for `--timeout`). - The container cannot bind to the configured HTTP port because it is already in use. - The static analysis engine (Spectra Core) failed to initialize due to insufficient resources. **Solution** 1. Inspect the container logs immediately after exit: ```bash docker logs ``` or for a Kubernetes pod: ```bash kubectl logs --previous -n ``` 2. Look for startup error messages. A missing license produces: ``` FATAL: License validation failed ``` Confirm that the `RL_LICENSE` environment variable is set and contains the full license file content: ```bash docker run -e RL_LICENSE="$(cat /path/to/license.lic)" \ registry.reversinglabs.com/fie/file-inspection-engine: ``` 3. Check for port conflicts if the log shows a bind error: ``` Error: listen tcp :8000: bind: address already in use ``` Change the host port mapping or stop the process occupying the port: ```bash docker run -p 9000:8000 ... # Map to a different host port ``` 4. Review the [Configuration Reference](./configuration.md) to verify that all provided flags use correct formats. Boolean flags require `=true` or `=false` when explicitly set (for example, `--cloud-updates=false`, not `--cloud-updates false`). 5. For Kubernetes deployments, check the Helm values for misconfigured environment variables or resource limits that are too low. See the [Helm Values Reference](./Deployment/Examples/values.md). --- ## `/readyz` returns 503 — not ready **Symptom** After starting the container, polling the readiness endpoint returns a non-200 status: ```bash curl http://localhost:8000/readyz # Returns 503 or another 5xx/4xx status ``` The container is running but not accepting file submissions. **Cause** - Threat data has not finished downloading. FIE requires the threat database to be available before it becomes ready, as described in [Starting the File Inspection Engine](./usage.md#starting-the-file-inspection-engine). - All Spectra Core instances are currently busy (high load or concurrency limit reached). - The license is invalid or has expired, preventing the engine from completing initialization. **Solution** 1. Check the container logs for readiness-related messages: ```bash docker logs -f ``` On first startup, you will see threat data download progress. Wait for the download to complete. The container logs `Instance is ready` when at least one analysis instance is available: ```json {"level":"info","process":"fie","instance_id":"core-regular-0.abc12","message":"Instance is ready"} ``` 2. Check the `/status` endpoint for more detail on the current state: ```bash curl http://localhost:8000/status ``` Review the `license.valid_until` field and the `spectra_core.available_regular_cores` field. If `available_regular_cores` shows `0/N`, all instances are busy or failed to initialize. 3. If the license has expired, update the `RL_LICENSE` environment variable and restart the container. See [License validation error on startup](#license-validation-error-on-startup). 4. For the `/readyz` endpoint behavior when under load, see [Request Rejection](./usage.md#request-rejection). The endpoint returns a non-200 status when memory or concurrency limits are exceeded — this is expected behavior, not a fault. --- ## Threat database download doesn't start, fails or is slow **Warning: By default, cloud updates are enabled (`--cloud-updates=true`). If cloud updates are disabled (`--cloud-updates=false`), the threat data download does not start at all. Scenarios where cloud updates are enabled, but the download fails or is slow are described below.** **Symptom** The container remains in a not-ready state, with the logs pointing to the threat data download as the cause. If the download encounters connectivity issues, the logs show repeated retry attempts before ultimately failing: - During retries: `Threat data update partially failed` - When retries are exhausted: `No more retries` - Final status: `Cloud update failed` **Cause** - Outbound HTTPS connectivity from the container to the ReversingLabs update infrastructure is blocked by a firewall or requires a proxy. - The `--without-malicious-threat-data` flag is not set, but network access to the update server is unavailable. - The container's DNS is not resolving the update server hostname. **Solution** 1. Test outbound connectivity from inside the container: ```bash docker exec curl -I https://data.reversinglabs.com ``` If this fails, the container cannot reach the update server. Check firewall egress rules and ensure that outbound HTTPS (port 443) is permitted. 2. If a proxy is required, configure it using the `--proxy-address` flag or the `RL_PROXY_ADDRESS` environment variable: ```bash docker run -e RL_PROXY_ADDRESS="http://proxy.company.internal:8080" \ -e RL_LICENSE="..." \ registry.reversinglabs.com/fie/file-inspection-engine: ``` 3. For air-gapped environments, use the offline threat data download process. See [Air-Gapped Kubernetes Deployment](./Deployment/air-gapped-kubernetes.md) for the procedure to pre-load threat data without internet connectivity. 4. For flaky connections, consider downloading the threat data manually the first time using the standalone `threat-data` application. It exposes the `RL_RETRY_COUNT` environment variable (default: 3), which controls how many times failed segment downloads are retried. The main FIE application does not expose `RL_RETRY_COUNT` directly. Instead, it retries at each cloud update interval (`--cloud-update-interval`, default: 5 minutes). 5. If downloads are slow, increase either the number of concurrent download connections using `--cloud-update-concurrency` or the `RL_CLOUD_UPDATE_CONCURRENCY` environment variable. This setting is supported in both the main FIE application and the standalone `threat-data` application. 6. If you want to run FIE without downloading malicious threat data (relying on static analysis only), set `--without-malicious-threat-data=true`. See the [Configuration Reference](./configuration.md) for the implications of this option. --- ## Analysis returns 503 Service Unavailable **Symptom** `POST /scan` requests return: ```http HTTP/1.1 503 Service Unavailable ``` or: ```http HTTP/1.1 429 Too Many Requests {"error":"The concurrency limit has been reached"} ``` or: ```http HTTP/1.1 429 Too Many Requests {"error":"Analysis not accepted due to high processing load"} ``` **Cause** - All Spectra Core instances are busy processing other files (high load). - The concurrency limit configured with `--concurrency-limit` has been reached. - Memory usage has exceeded the `--processing-unavailable-at-memory-percent` threshold. **Solution** 1. Review the [response status codes](./usage.md#possible-response-status-codes). A 429 with `"The concurrency limit has been reached"` means too many simultaneous requests are active; retry after a short delay. 2. Monitor the `/status` endpoint to see current instance availability: ```bash curl http://localhost:8000/status | python3 -m json.tool | grep -A4 "spectra_core" ``` The `available_regular_cores` and `available_large_cores` fields show how many instances are currently free. 3. Implement retry logic with backoff in your client for 429 responses. Do not retry at a constant rate under load — this worsens congestion. 4. Increase the number of Spectra Core instances (`--number-of-regular-cores`) to handle higher concurrency, subject to available CPU and memory. See the [Configuration Reference](./configuration.md). 5. Check logs for the high-load indicators described in [Logging](./usage.md#logging): ```json {"level":"warn","process":"core","message":"High processing load"} ``` Wait for `"High processing load over"` before resuming normal submission rates. 6. For sustained high throughput, consider deploying multiple FIE instances behind a load balancer, with each instance's `/readyz` endpoint used as the health check. --- ## Port binding conflict **Symptom** The container fails to start with an error in the logs: ``` Error: listen tcp :8000: bind: address already in use ``` or Docker reports: ``` docker: Error response from daemon: driver failed programming external connectivity: Bind for 0.0.0.0:8000 failed: port is already allocated. ``` **Cause** - Another process on the host is already using port 8000 (the default FIE HTTP port). - A previous FIE container is still running and holding the port. - The Docker daemon has reserved the port range that includes 8000. **Solution** 1. Identify what is using the port: ```bash sudo lsof -i :8000 sudo ss -tlnp | grep 8000 ``` 2. If an old FIE container is occupying the port, stop it: ```bash docker ps -a | grep fie docker stop docker rm ``` 3. Map the container to a different host port without changing the internal port: ```bash docker run -p 9001:8000 \ -e RL_LICENSE="..." \ registry.reversinglabs.com/fie/file-inspection-engine: ``` 4. To change the port the FIE process listens on internally, use the `--http-address` flag: ```bash docker run -p 9001:9001 \ -e RL_HTTP_ADDRESS=":9001" \ -e RL_LICENSE="..." \ registry.reversinglabs.com/fie/file-inspection-engine: ``` See the [Configuration Reference](./configuration.md) for the `--http-address` option. --- ## Out of memory (OOM) — container killed **Symptom** The container is killed abruptly during analysis. Docker events or Kubernetes events show: ``` OOMKilled ``` or the host `dmesg` contains: ``` Out of memory: Kill process (fie) score or sacrifice child ``` **Cause** - The container memory limit is too low for the number of Spectra Core instances and the file types being analyzed. - Files with very high decompression ratios (deeply nested archives) are consuming more memory than expected. - The temporary directory is mounted as `tmpfs`, which counts toward container memory usage. **Solution** 1. Increase the container memory limit. As a general guideline, allocate at least 1–2 GB of memory per Spectra Core instance, plus overhead for the FIE process itself. For Docker: ```bash docker run --memory="8g" ... ``` For Kubernetes, update the resource limits in the Helm values. See the [Helm Values Reference](./Deployment/Examples/values.md). 2. If `tmpfs` is used as the temporary directory, its contents count toward container memory. Consider switching to a host-mounted volume for temporary files to avoid this. 3. Enable the memory threshold check using `--processing-unavailable-at-memory-percent`. This causes FIE to reject new submissions when memory usage is high, preventing OOM rather than being killed: ```bash docker run -e RL_PROCESSING_UNAVAILABLE_AT_MEMORY_PERCENT=85 ... ``` When memory exceeds 85%, the engine logs: ```json {"level":"warn","message":"Memory use is above the threshold of 90%"} ``` and starts returning HTTP 429 to new submissions. See [Memory Usage](./usage.md#memory-usage). 4. Reduce the number of concurrent Spectra Core instances (`--number-of-regular-cores`) to lower peak memory consumption. 5. Review the [platform requirements](/General/DeploymentAndIntegration/PlatformRequirements) for recommended memory allocations based on instance count and expected file types. --- ## Container restarts because of cgroup v2 `memory.oom.group` **Symptom** The Kubernetes node restarts the entire FIE pod when one Spectra Core engine hits its memory limit. Logs show a single engine OOM, but the container is removed rather than just the failing process. **Cause** - Kubernetes v1.28+ defaults the node-level `memory.oom.group=1`, so any OOM in the pod kills every process in that cgroup. FIE enables concurrent Spectra Core instances inside the same pod, and cgroup v2 enforces the group-wide kill. This differs from cgroup v1 behaviour where only the oom-ing process (engine) was restarted. - The behavior is driven by the node’s kubelet configuration and is not something the FIE image can change. **Solution** If you are deploying on Google Kubernetes Engine (GKE), you can restore the cgroup v1-style behavior where only the offending process is killed: 1. Enable the kubelet `singleProcessOOMKill` option on your node pools. This setting is available starting with GKE versions `1.32.4-gke.1132000` and `1.33.0-gke.1748000`. 2. Follow the Google Cloud documentation for [Customizing node system configuration](https://cloud.google.com/kubernetes-engine/docs/how-to/node-system-config) to apply the `singleProcessOOMKill: true` toggle. 3. After the nodes pick up the new kubelet config, pods experiencing isolated engine OOMs should only restart the affected Spectra Core process instead of the entire container. The pod will still log the original OOM event and should recover once the engine restarts. 4. Continue sizing memory and core counts according to the [platform requirements](/General/DeploymentAndIntegration/PlatformRequirements), since `singleProcessOOMKill` only affects how kubelet responds to the OOM—it does not prevent the underlying memory condition. --- ## Large files time out during analysis **Symptom** Analysis of files above a certain size returns: ```http HTTP/1.1 524 {"error": "The analysis could not be completed within the configured maximum analysis time"} ``` The container logs show: ```json {"level":"warn","message":"Analysis aborted due to a timeout"} {"level":"warn","message":"Analysis has timed out"} ``` **Cause** - The `--timeout` value is too short for the complexity of the file being analyzed. - A very large or deeply nested archive requires more time to unpack and analyze than the timeout allows. - After a timeout, the Spectra Core instance handling the file is restarted, temporarily reducing available capacity. **Solution** 1. Increase the analysis timeout using the `--timeout` flag. Duration values use `s`, `m`, or `h` suffixes: ```bash docker run -e RL_TIMEOUT="5m" ... ``` Note: very short timeout values are not recommended because instance restarts after a timeout can cause cascading delays. See [Timeouts](./usage.md#timeouts). 2. After a timeout, the affected instance restarts automatically. Monitor logs for `"Instance is ready"` to confirm recovery: ```json {"level":"info","message":"Instance is ready"} ``` 3. For predictably large files, configure a dedicated large-file instance pool using `--number-of-large-cores` and `--large-file-threshold`. These instances process one file at a time, and their separate timeout can be tuned independently: ```bash docker run \ -e RL_NUMBER_OF_LARGE_CORES=2 \ -e RL_LARGE_FILE_THRESHOLD=50 \ -e RL_TIMEOUT="10m" \ ... ``` See the [Configuration Reference](./configuration.md) for all large-file pool options. 4. Use the [Check for Hard Timeout](./usage.md#check-for-hard-timeout) procedure to distinguish regular timeouts from hard timeouts caused by Spectra Core process termination. --- ## License validation error on startup **Symptom** The container exits immediately or the `/readyz` endpoint returns a non-200 status. Container logs contain: ``` FATAL: License validation failed ``` or: ``` License expired ``` The `/status` endpoint returns a `valid_until` date in the past. **Cause** - The `RL_LICENSE` environment variable is not set. - The license file content is truncated, incorrectly formatted, or was copied with extra whitespace or line breaks. - The license has reached its expiration date. - For network-validated licenses, the container cannot reach the ReversingLabs license server. **Solution** 1. Confirm the `RL_LICENSE` environment variable is set. Pass the license as the entire file contents: ```bash # Using a license file on disk docker run -e RL_LICENSE="$(cat /path/to/rl-license.lic)" \ registry.reversinglabs.com/fie/file-inspection-engine: ``` For Kubernetes, store the license as a Secret and reference it in the pod spec: ```bash kubectl create secret generic fie-license \ --from-file=RL_LICENSE=/path/to/rl-license.lic ``` 2. Verify the license has not expired using the `/status` endpoint: ```bash curl http://localhost:8000/status | python3 -m json.tool | grep valid_until ``` 3. If the license is expired, contact your ReversingLabs account manager or [support@reversinglabs.com](mailto:support@reversinglabs.com) to obtain a renewed license. 4. Note that `RL_LICENSE` is only available as an environment variable, not as a CLI flag. See the [Configuration Reference](./configuration.md) for the `RL_LICENSE` parameter notes. --- ## Analysis results show UNKNOWN for all files **Symptom** All files submitted to `/scan` return `"classification": "OK"` regardless of file type, and no malicious verdicts are produced even for files known to be malicious. **Cause** - The `--without-malicious-threat-data=true` flag is set, which disables downloading of malicious threat data and prevents malicious classifications from threat data matching. - Threat data has not yet downloaded successfully, so the engine is operating without a populated database. - The threat database timestamp is very old (stale), indicating updates have not been applied for an extended period. **Solution** 1. Check the current threat data configuration and status using `/status`: ```bash curl http://localhost:8000/status | python3 -m json.tool ``` Review the `threat_data.enabled_classifications` field. If it shows an empty array (`[]`), malicious classification from threat data is disabled. The `version.threat_data` field shows when the database was last updated. 2. If `enabled_classifications` is empty, check whether `--without-malicious-threat-data=true` is set in your configuration. Remove this flag (or set it to `false`) if you want malicious threat data to be used: ```bash docker run -e RL_WITHOUT_MALICIOUS_THREAT_DATA=false ... ``` 3. If threat data is enabled but stale, verify that cloud updates are working. Check `--cloud-updates` is not set to `false` and that the container can reach the update server. See [Threat database download fails or is slow](#threat-database-download-fails-or-is-slow). 4. Note that with `--without-malicious-threat-data=false` (the default), FIE still classifies files using [Spectra Core](/General/AnalysisAndClassification/SpectraCoreAnalysis) static analysis, so some malicious files will be detected even without threat data. However, threat data significantly improves detection coverage. --- ## `/status` endpoint shows zero available instances **Symptom** The `/status` endpoint shows all Spectra Core instances as unavailable: ```json { "spectra_core": { "available_regular_cores": "0% (0/4)", "available_large_cores": "0% (0/2)" } } ``` All `/scan` requests are being rejected with 429 or 503. **Cause** - All instances are busy processing files submitted simultaneously. - One or more instances have timed out and are in the process of restarting. - All instances failed to initialize during startup (for example, due to resource exhaustion). **Solution** 1. Wait briefly and re-check `/status`. Instances that are restarting after a timeout typically recover within a few seconds. Look for `"Instance is ready"` log messages: ```bash docker logs -f | grep "Instance is ready" ``` 2. If instances are busy (not restarting), reduce the rate of incoming requests and allow in-flight analyses to complete. Check the concurrency limit (`concurrency_limit` in `/status`) and compare it to the number of active instances. 3. If instances failed during startup, check logs for initialization errors: ```bash docker logs 2>&1 | grep -i "error\|fatal\|failed" ``` 4. Check for [OOM conditions](#out-of-memory-oom--container-killed) — if instances are being killed by the kernel before they can finish initializing, the available count will remain at zero. 5. For a persistent `0/N` state where all instances are stuck, restart the container. If this state recurs, review the [platform requirements](/General/DeploymentAndIntegration/PlatformRequirements) to ensure the host has sufficient CPU and memory for the configured number of instances. 6. For Kubernetes deployments, check whether the pod itself is in a degraded state: ```bash kubectl describe pod -n kubectl top pod -n ```