Skip to main content
Wildo.ai Coming soon

Files and storage

Keep a file unavailable until its scan finishes

Required scans block downloads and new attachments until a clean result. Standard forms wait while the file is being checked.

An attachment waits for scanning before becoming downloadable.

Keep a file unavailable until its scan finishes

Accepting a file and allowing people to use it are different steps. Wildo can place an uploaded file into a scanning state before making it available, then release or quarantine it according to the result.

The field chooses whether scanning is required or skipped; configured scanner infrastructure supplies the checks. Standard forms show that processing is still underway and wait before adding the new file to the record. A rejected file stays out of the record, with an error the person can act on.

Example — A document waits for a clean result

A contract finishes uploading while its scan is still running. The standard form keeps the new attachment pending and blocks saving until processing resolves. A clean result lets the person save the record with its attachment. An infection or scanner error leaves the file unavailable; the person can discard the rejected selection and choose another file.

For engineers

Declare policy and provision its scanner

Use the file’s scanPolicy for an explicit requirement or exception. An unspecified field inherits the application’s configured scanner posture. The backend reads this decision in file-scanning.backend.service.ts; the separate global predicate is included, with intervening commentary omitted.

shouldScan(fileMetadata?: FileSchemaMetadata): boolean {
  const scanPolicy = fileMetadata?.scanPolicy;

  if (scanPolicy === 'required') return true;
  if (scanPolicy === 'skip') return false;

  return this.isGlobalScanningEnabled();
}
isGlobalScanningEnabled(): boolean {
  if (!this.appConfigService.isInitialized()) return false;
  return this.clamavScanner.isConfigured() || this.yaraScanner.isEnabled();
}

Move into scanning before dispatching work

The upload handler awaits scan initiation before returning upload acceptance and file metadata. Upload acceptance does not mean the scan has finished: file.readiness reports whether the returned file can be attached. Inside the scan service, the status transition occurs before asynchronous scanner execution. This excerpt omits validation above and the error-logging callback below.

await this.filesService.updateFileStatus(
  fileId,
  FileStatus.SCANNING,
  executionContext,
);

if (onClean) {
  this.onCleanCallbacks.set(fileId, onClean);
}

this.logDebug('File transitioned to SCANNING, dispatching scan', { fileId });

Distinguish a clean result from a failed check

Configured ClamAV signature scanning runs first; enabled YARA pattern scanning follows if the earlier layer has not refused the file. The pipeline requires at least one configured scanner when scanning is requested. Startup validation reports incompatible required-field and scanner configurations, and the runtime also refuses to certify a file clean when no scanner is available.

A clean verdict transitions the file to CLEAN and releases configured follow-on work. An infection moves it through INFECTED toward QUARANTINED; quarantine keeps the stored bytes but blocks normal download. A scanner or storage error moves it to FAILED, requiring recovery or re-scan. The same state checks stop linking while scanning. Standard file controls refresh pending metadata and publish the new reference only after it becomes ready; the form also preserves the processing error through submit validation. Scanning reduces malware risk; it does not validate the business meaning or confidentiality of a document.

Read readiness before attaching a file

Upload responses include file.readiness; the authorized metadata endpoint returns the same FileMetadata contract. The standard single- and multiple-file controls handle this for local files and provider selections. Custom upload interfaces must inspect readiness rather than treat HTTP success as permission to attach.

file.readiness valueMeaning for the form
readyThe file may be submitted as a new attachment, subject to server authorization
processingRefresh metadata; do not submit the new reference yet
attachedRetain an existing attachment; this does not authorize attaching it to another record
unavailableDo not submit the new reference; show the failure and allow another selection

Readiness describes attachment eligibility, not download authorization. The backend still checks access and lifecycle state when linking or serving a file. Polling observes a scan; it does not start a new scan or automatically retry a failed one. With scanning skipped or inactive, an ordinary completed upload can already report ready.

Match field policy to the startup contract

Field policyRequired setupResult
requiredConfigured ClamAV; YARA alone does not satisfy startup validationThe upload must pass scanning
Unspecified / inheritedApplication scanner posture; configured ClamAV or enabled YARA activates global scanningInherits that posture
skipExplicit field exceptionSkips this scanning path
Scan outcomeFile state and evidence
CleanCLEAN, with configured follow-on work released; this path does not emit a separate clean audit event
MalwareInfection/quarantine handling and a malware audit event
Scanner or storage failureFAILED and failure evidence; an error is not a clean verdict

Use status for current download eligibility and the emitted failure/malware events for investigation. The absence of a clean-event row must not be interpreted as proof that no scan ran.

Connect a required field to the deployed scanner

This illustrative attachment field requires scanning, independently of the inherited application posture:

import { z_file } from '@wildo-ai/zod-decorators';

const attachment = z_file({
  multiple: false,
  allowedMimeTypes: ['application/pdf'],
  scanPolicy: 'required',
});

Provision a ClamAV daemon reachable from the backend, then supply its address through the application’s environment setup. For example, these values assume the backend can resolve a service named clamav; setting them does not install or start the daemon:

CLAMAV_HOST=clamav
CLAMAV_PORT=3310

The configuration loader seeds fileScanning.antivirus; the scanner reads that resolved configuration. Startup validation checks required fields against ClamAV configuration. A configured address is not proof that the scanner is healthy: the upload path still needs the daemon to accept and complete a scan.

For the additional YARA layer, provision the YARA scanning service and its rules, then configure both the enable flag and its reachable address:

YARA_ENABLED=true
YARA_HOST=yara
YARA_PORT=8080

Those names are illustrative deployment addresses. The YARA service consumes fileScanning.yara.enabled and fileScanning.yara.connection; enabling it without a host is a configuration problem. YARA alone does not satisfy the current startup requirement for a field marked required.

What you configureWhat to verify
Required field plus ClamAVStartup accepts the configuration; an uploaded file moves through processing to a clean or refused outcome
Additional YARA serviceEnabled flag, reachable service, and the intended rules; a clean ClamAV result does not bypass an enabled second layer
Custom upload interfacePoll authorized metadata while readiness is processing; submit the reference only when ready
Scanner outageThe file must not be treated as clean merely because the upload request succeeded

Retain the uploaded file ID while observing processing. Repeated metadata reads observe the existing scan; uploading again creates another upload rather than repairing the first scan.

Building a B2B product or an internal tool?

Wildo is not self-service yet. Tell us what you have in mind and we will say plainly whether it fits, and what happens next.