Skip to content

GrydFiles Module ​

GrydFiles is the platform's record of a file: who uploaded it, the hash the server computed, what the antivirus said, who still points at it and when it may die. The bytes travel between the client and the bucket by presigned URL and never through the API.

The decisions behind it are in ADR 0010; fields, routes, error codes and invariants are in the specification. These pages are about using and running the module.

The gap it closes ​

The framework could already talk to S3, Azure Blob Storage and Google Cloud Storage and issue presigned URLs. What no module had was the record around the bytes. GrydReports kept the output of a report as a URI in a string; GrydNotifications kept each e-mail attachment as byte[] in a column. Neither had a hash, an antivirus verdict, a retention rule or an answer to "does anybody still need this?" — and without that answer nothing can ever be purged safely. Both now store their files here.

✨ Features ​

FeatureDescription
🚚 Two-phase uploadReserve (POST /files), PUT straight to the bucket, confirm (POST /files/{id}/complete)
🦠 Quarantine before referencePending → Scanning → Available; nothing is referenceable without a verdict, and there is no manual release
🔑 Server-side identitysha256 computed on the one read that also feeds the type detector and the engine
🧪 Content-type checkBy content signature, OOXML package or text validation — never by extension; unconditional, even with the antivirus waived
♻️ Dedupe inside a tenantOne blob per content per tenant; never across tenants
🔗 Reference registryConsumers register what they hold (ownerScope + ownerKey); the count is what purges read
⏳ Declared retentionUntilReleased, RetainUntil or Permanent, from the upload or the profile; dates only move forward; legal hold
🪦 Purge with a tombstoneThe object dies, the record keeps hash, size, type, profile, scan result and location
📥 Short download URLsattachment + application/octet-stream, a few minutes of validity, a line in the audit trail per URL
🩺 Scanner healthGET /files/scanner-status, a health check and two alerts: queue and signature age
🏢 Multi-tenantTenant at the root of every key; another tenant's file is answered exactly as a missing one
🧩 In-process consumersModules in the same host use IFileService or the server-side store, with no HTTP

📦 Packages ​

📦 GrydFiles
├── GrydFiles.Core                    # StoredFile, FileReference, FileScanAttempt, error codes, options
├── GrydFiles.Application             # IFileService, IServerFileStore, profiles, scan contract (IFileScanner)
├── GrydFiles.Infrastructure          # EF Core (PostgreSQL), the scan job and the five recurring jobs
├── GrydFiles.API                     # FilesController — the ten routes
├── GrydFiles.Infrastructure.ClamAv   # The clamd adapter and the generated clamd.conf
└── GrydFiles (meta-package)          # Core + Application + Infrastructure + API
Depends onWhy
Gryd.Infrastructure object storage (AddGrydObjectStorage)The bucket, the provider and the presigned URLs
GrydJobsThe scan queue and the recurring jobs
An IAuditStoreDownload URLs, quarantines, purges and legal-hold releases are written to the trail
PostgreSQLThe module's own schema; the partial unique indexes of the dedupe are PostgreSQL's

🔄 The life of a file ​

Infected is terminal: no permission, admin:system included, brings a file back from it. Failed never releases anything either; it only waits for another pass.

🚀 Where to go next ​

PageWhat it covers
Getting StartedRegistration, migrations and the minimal flow
ConfigurationThe GrydFiles section, profiles and the scanner limits
API ReferenceThe ten routes, the error contract and the permissions
Antivirus & QuarantineRunning clamd, the retry budget and the two alerts
DownloadURL validity, the two headers and the trail
Retention & PurgeHow a file dies, and the consistency sweep
Specificationv1.2, with the errata of the implementation

Released under the MIT License.