Skip to content
AI Primer
release

Files SDK 1.8 adds files.search() and 7 storage plugins

Files SDK 1.8 shipped cross-adapter search plus typed plugins for compression, encryption, dedup, tracing, usage metering, validation, and versioning. Use it to compose storage hooks and power CLI and MCP search flows.

4 min read
Files SDK 1.8 adds files.search() and 7 storage plugins
Files SDK 1.8 adds files.search() and 7 storage plugins

TL;DR

  • Files SDK 1.8 adds files.search() across every adapter, and haydenbleasel's launch thread says it supports glob, regex, substring, exact match, and RegExp, while the search docs note that it streams matches as an async iterable.
  • The bigger architectural change is the new plugin system, which haydenbleasel's thread describes as typed and opt-in, and the plugin overview says each plugin can wrap every operation or extend the instance with new methods.
  • Seven built-in storage plugins shipped on day one, according to the launch thread: compression, encryption, dedup, tracing, usage metering, validation, and versioning.
  • Several of those plugins push storage concerns up into the SDK layer: haydenbleasel's encryption post adds envelope encryption, the usage post meters bytes and ops lazily, and the validation post can block unsafe writes before bytes reach an adapter.

You can jump straight to the search API, browse the new plugin overview, and the release PR spells out why several features stay adapter-agnostic by relying on metadata, Web Crypto, Compression Streams, and optional OpenTelemetry. One nice detail from haydenbleasel's thread is that files.search() now powers both the CLI and MCP search tools, which turns a storage helper into agent plumbing fast.

files.search(pattern, options?) is a cross-adapter object finder. According to haydenbleasel's thread, it matches by glob, regex, substring, exact match, or RegExp.

The search docs add the implementation detail the tweet only hints at: matches stream as an async iterable on top of listAll, stay memory-bounded on large buckets, and push a glob's literal prefix down automatically. The same docs say matching happens against the caller-facing key, after any instance prefix is stripped.

Plugin pipeline

The plugin system wraps Files SDK operations in what haydenbleasel's thread calls an ordered onion. The official overview is more precise: a plugin can wrap calls to transform, veto, or observe operations, and it can extend the instance with new namespaced methods.

That split matters because 1.8 is not just a bag of middleware. Some plugins alter I/O paths, while others add fresh surface area such as files.usage(), files.usageByGroup(), files.versions(), and files.restore(), per the plugin docs, usage docs, and versioning docs.

Storage transforms

Three of the shipped plugins change how bytes are stored:

  • compression(), which haydenbleasel's thread introduced, compresses uploads with gzip by default, stores the compressed form only if it is smaller, and records the algorithm plus original byte length in metadata, according to the compression docs.
  • encryption(), which haydenbleasel's encryption post says is provider-agnostic, uses envelope encryption with a per-object data key and AES-256-GCM, while the encryption docs say it relies on the Web Crypto API and decrypts transparently on reads.
  • dedup(), which haydenbleasel's thread describes as content-addressed storage, hashes uploads with SHA-256 and stores one shared blob under .dedup/, leaving logical keys as tiny pointers, per the dedup docs.

The common pattern is that Files SDK is using metadata plus wrapper logic to fake capabilities that many object stores expose inconsistently, or not at all.

Guardrails and telemetry

The guardrail side is unusually concrete for a storage SDK release.

  • contentType() inspects upload bytes instead of trusting the client, and the content type docs say it can either correct the stored MIME type or reject mismatches so mislabeled HTML or SVG does not get served inline as an image.
  • validation(), per haydenbleasel's validation post, is fail-closed before bytes move. The validation docs say it can enforce size bounds, allowed MIME types, key naming rules, copy and move destination rules, and signed-upload restrictions.
  • tracing(), according to haydenbleasel on tracing, opens one OpenTelemetry span per operation under the active request span. The tracing docs say it records attributes like key and outcome, and keeps @opentelemetry/api as an optional peer dependency.
  • usage(), which haydenbleasel's post framed as SDK-layer metering, tracks uploads, operation counts, and bytes read from downloads only as consumers actually read the stream, according to the usage docs.

Versioning

The last built-in plugin is also the one that adds the most user-facing behavior. haydenbleasel's versioning post says versioning() snapshots bytes before overwrite or delete, exposes files.versions(key) and files.restore(key, versionId?), and hides stored snapshots from normal list() output.

The versioning docs fill in the missing mechanic: before an upload, delete, or the destination side of a copy or move clobbers an object, the plugin server-side copies the old bytes to a timestamped key under a hidden prefix. That gives Files SDK a rollback story without buffering or transforming bodies, and without requiring adapter-native version history.

Further reading

Discussion across the web

Where this story is being discussed, in original context.

On X· 2 threads
TL;DR1 post
Guardrails and telemetry2 posts
Share on X