Files SDK 1.9 adds Neon adapter, failover, and typed ValidationError
Files SDK 1.9 shipped a Neon adapter plus plugins for audit logging, caching, failover, signed URL policy, soft delete, tiering, and ZIP workflows. The release makes the storage API more production-ready for multi-backend uploads and safer presigned URL handling.

TL;DR
- Files SDK 1.9 shipped a first class Neon adapter, so the same upload, download, and presigned URL API used for S3, R2, or GCS now works against Neon object storage with one line of config, according to haydenbleasel's Neon adapter post and haydenbleasel's release thread.
- The release adds three production plumbing plugins, because haydenbleasel's audit post makes mutations fail closed if audit sinks fail, haydenbleasel's cache post caches repeated
head()andurl()calls, and haydenbleasel's failover post retries against secondary backends only when the primary is actually down. - Safer presigned URL handling is now a plugin instead of ad hoc app code, since haydenbleasel's signedUrlPolicy post forces attachment disposition by default, clamps expiry windows, and can cap signed upload sizes.
- Upload validation now throws a dedicated error type, and haydenbleasel's ValidationError post says the new
reasondiscriminant distinguishes size, type, and key failures without string parsing. - Recovery and cold storage workflows also moved into the core package, with haydenbleasel's softDelete post, haydenbleasel's tiering post, and haydenbleasel's zip post adding recycle-bin deletes, hot/cold routing, and dependency-free ZIP streams.
You can jump straight to the Neon adapter docs, the failover plugin page, and the ZIP docs. The merged 1.9.0 release PR is also unusually useful here, because it spells out the release scope and dates instead of making you reconstruct the whole thing from the thread.
Neon adapter
The headline addition is a Neon adapter that keeps API parity with the package's other S3 style backends. According to haydenbleasel's Neon adapter post, it uses the AWS_* credentials Neon injects and keeps the same upload, download, delete, url(), and signedUploadUrl() surface.
The linked adapter docs add one implementation detail that matters if you actually wire it up: Neon uses path-style addressing, so the bucket travels in the request path instead of the hostname.
Audit, cache, and failover
Files SDK 1.9 gets a lot more opinionated about storage behavior under failure.
audit()awaits a structured record for each mutation, and haydenbleasel's audit post says successful writes can still fail closed if the audit sink rejects.cache()targets cheap reads first, because haydenbleasel's cache post limits default caching to repeatedhead()andurl()calls, while smalldownload()caching stays opt in.failover()only activates on backend outages, and haydenbleasel's failover post says real 404s and auth failures are returned as is instead of being masked by a secondary.
The official failover docs add the main caveat the tweet thread skipped: writes served by a secondary during an outage are not reconciled back to the primary automatically.
Signed URL policy and ValidationError
Two changes in 1.9 are really about deleting boring glue code from app layers.
signedUrlPolicy()turns safer defaults into a wrapper, and haydenbleasel's signedUrlPolicy post says it forces attachment downloads by default, caps URL lifetimes, and can enforce a maximum upload size onsignedUploadUrl().validation()now throwsValidationError, and haydenbleasel's ValidationError post says callers can branch onreason: "size" | "type" | "key"instead of parsing error strings.
The linked validation docs add one more useful detail: signed upload URL generation itself fails closed when size or type rules are configured, which shuts down an easy bypass path.
Soft delete, tiering, and ZIP
The back half of the release is a grab bag, but it is a practical one.
softDelete()moves deleted objects into a trash prefix, and haydenbleasel's softDelete post says you can list, restore, or purge those objects later.tiering()splits one logical namespace across hot and cold adapters, while haydenbleasel's tiering post says reads, writes, copy, move, and merged listing all stay behind the same Files API.zip()adds streaming archive creation, archive writes withzipTo(), and extraction withunzip(), and haydenbleasel's zip post says the implementation rejects zip-slip paths and has no native dependencies.
That last bit is the nicest surprise in the release. The ZIP docs say zip() streams lazily, but unzip() verifies CRC and size for extracted entries, which is a lot more serious than the usual "here's a helper for bundling files" feature.