Table of Contents

How does it work with Neos

Report generation is asynchronous and should not be performed synchronously when calling the generation API in the report server. The generation is performed by the reporting service for a business cluster.

Report templates are stored in Neos cluster backend service as .mrt files.

Report Persistence Modes

The reporting service supports three report persistence modes with automatic mode selection based on configuration:

Mode Priority and Detection

In production (Helm deployment), the active mode is selected with the following priority order:

  1. S3 Mode (priority 1) - Enabled when report.S3.configurationSecret is configured
  2. Filesystem Mode (priority 2) - Enabled when report.volume is configured
  3. Legacy Mode (priority 3) - Enabled otherwise (neither report.S3.configurationSecret nor report.volume is configured)
Important

S3 mode is intended for business cluster versions 3.2 and later. Older business clusters can still run in an S3-configured environment by setting clusters[].reportLegacyMode: true (mono-tenant) or clusters[].versions[].reportLegacyMode: true (multi-tenant), which keeps their report flow on the legacy Pub/Sub payload path.

At runtime, the application detects S3 mode from AWS:BucketName and legacy mode from NeosReportLegacyMode=true. When S3 is active, the reporting server can still force the legacy payload flow for specific requesters using NeosReportLegacyRequesterAppIds. This list is rendered by Helm from reportLegacyMode flags and matches Dapr app IDs (backend and task-runner) of targeted legacy clusters/versions. In production, these runtime settings are expected to be rendered/provided by Helm from the deployment configuration above.

S3 Mode

When it's active: report.S3.configurationSecret is configured in Helm. The secret content is then projected into the process configuration, where AWS:BucketName becomes the runtime signal used by the application.

Reports are persisted directly in an AWS S3 bucket as objects with the following characteristics:

  • Object location: reports/<TenantId>/<GenerationId> in the configured bucket
  • Metadata: Report information (filename, tenant, user, report name) is stored as S3 object metadata
  • Requirements: Only BucketName is required; AWS SDK handles authentication via IAM roles, environment variables, or credential profiles
  • Optional parameters: ServiceUrl (for S3-compatible endpoints like MinIO), AccessKey/SecretKey (for explicit credentials), Region (for explicit region override)
  • Advantages: Scalable, serverless storage; supports large files without size limits; supports multi-region access; ideal for cloud-native deployments

Filesystem Mode

When it's active: report.volume is configured in Helm (and no S3 configuration secret is configured).

Reports are persisted on a shared distributed filesystem with the following characteristics:

  • Storage path: /var/lib/neos/reports/<TenantId>/<GenerationId> in production, .neos/reports/<TenantId>/<GenerationId> in development
  • Metadata: Report information is stored in a companion .meta file (JSON format)
  • Write safety: Report is written as <GenerationId>.tmp and renamed to <GenerationId> when complete, preventing partial file retrieval
  • Requirements: Persistent volume mounted at /var/lib/neos/reports in Kubernetes deployments
  • Advantages: Familiar POSIX filesystem semantics; supports large files; suitable for on-premises deployments with shared storage

Legacy Mode

When it's active: Neither report.S3.configurationSecret nor report.volume is configured in Helm. In this case, Helm renders NeosReportLegacyMode=true in application settings, and runtime mode detection falls back to legacy mode.

Reports are persisted in the cluster database via the reporting message:

  • Storage location: Cluster database $NeosFile datatable
  • Transport: PDF is base64-encoded and embedded in the PubSub message payload
  • File size limit: 4MB per report due to message payload constraints
  • Deprecation notice: This mode is retained for backward compatibility with versions prior to 2.4.0 and should not be used in new deployments
  • Use case: Legacy systems without persistent volumes or S3 access

Per-request Legacy Fallback in S3 Environments

When S3 mode is active, report persistence is normally done in S3. However, for compatibility with older business clusters, the reporting server can force legacy transport for selected requesters:

  • Activation source: clusters[].reportLegacyMode: true (mono-tenant) or clusters[].versions[].reportLegacyMode: true (multi-tenant)
  • Helm rendering: these flags generate NeosReportLegacyRequesterAppIds in report server appsettings.json
  • Matching rule: if request Interlocutors.ActualRequester or Interlocutors.BusinessDataProvider matches a configured app ID, report generation switches to legacy payload for that request
  • Result: PDF is base64-encoded in ReportingGenerated payload, and consuming legacy backend persists it into $NeosFile

Generation Flow

When a client requests a report to be generated:

  1. Request initiation: Backend cluster sends a request to the reporting service using pub sub
  2. Report generation: Reporting service publishes its own event and manages its task queue
  3. Template & data retrieval: Reporting service uses service invocation to:
    • Retrieve the template (.mrt file) from the cluster
    • Query the cluster's entity views needed to render the report as PDF
  4. Report persistence: Based on the active mode (S3, Filesystem, or Legacy), the generated report is saved:
    • S3 Mode: Direct upload to S3 bucket with object metadata
    • S3 Mode + per-request legacy fallback: Base64 payload is used for requesters listed in NeosReportLegacyRequesterAppIds
    • Filesystem Mode: Written to shared filesystem with companion .meta file
    • Legacy Mode: Base64-encoded and published to PubSub message
  5. Notification: Reporting service publishes a message with the report identifier to the message broker
  6. Client notification: Cluster subscribes to the message and notifies the requesting client via SignalR
  7. Download: When the client requests download/print:
    • S3 Mode: Browser downloads from the reporting service which retrieves the report from S3 and serves it
    • Filesystem Mode: Browser downloads from the reporting service which retrieves the report from the shared filesystem and serves it
    • Legacy Mode: Browser downloads from the cluster backend which retrieves the report from the database and serves it

Flow diagram

sequenceDiagram
    participant WB as Web browser
    participant CB as Cluster backend
    participant MB as Message broker (Dapr pub sub)
    participant RS as Reporting server
    participant NH as Notification Hub (SignalR)
    participant S3 as AWS S3 (optional)
    WB->>RS: Request report generation
    RS->>MB: Publish event GenerateReport
    MB->>RS: Subscribe event GenerateReport

    rect rgb(18, 21, 230)
        Note right of CB: GenerateReport
        RS->>CB: Get report definition (mrt file) via DAPR service invocation
        CB-->>RS: Return definition
        RS->>CB: Get report data from entity views via DAPR service invocation
        CB-->>RS: Return data
        alt S3 Mode (report.S3.configurationSecret configured)
            alt Requester in NeosReportLegacyRequesterAppIds
                RS->>MB: Publish generated PDF file (base64 encoded)
            else Standard S3 flow
                RS->>S3: Upload report with object metadata
                S3-->>RS: Confirm upload
                RS->>MB: Publish generated PDF identifier (S3 location)
            end
        else Filesystem Mode (report.volume configured)
            RS->>RS: Save report at `/var/lib/neos/reports/<TenantID>/<GUID>`
            RS->>RS: Write `<GUID>.meta` with report metadata
            RS->>MB: Publish generated PDF identifier (file path)
        else Legacy Mode (no report.volume and no report.S3.configurationSecret)
            RS->>MB: Publish generated PDF file (base64 encoded)
        end
    end

    rect rgb(235, 52, 83)
        Note right of WB: ReportingGenerated
        MB->>CB: Subscribe event ReportingGenerated
        opt Legacy Mode (global or per-request fallback)
            CB->>CB: Store PDF from base64 to database
        end
        CB->>NH: Create notification (SignalR)
        NH->>WB: Push notification for PDF download/print
    end

    rect rgb(238, 130, 238)
        Note right of WB: Download/Print PDF
        alt S3 Mode (Browser downloads from reporting service)
            WB->>RS: Request PDF download/print
            RS->>S3: Retrieve report from S3
            S3-->>RS: Return PDF
            RS-->>WB: Serve PDF
        else Filesystem Mode (Browser downloads from reporting service)
            WB->>RS: Request PDF download/print
            RS->>RS: Retrieve report from `/var/lib/neos/reports/<TenantID>/<GUID>`
            RS-->>WB: Serve PDF
        else Legacy Mode (Browser downloads from cluster backend)
            WB->>CB: Request PDF download/print
            CB->>CB: Retrieve PDF from database
            CB-->>WB: Serve PDF
        end
    end

Note: this diagram describes the Helm deployment contract used in production. At runtime, the application detects S3 mode from AWS:BucketName and legacy mode from NeosReportLegacyMode=true.