Files
tech-log-backend/src/adapter/outbound/objectstorage

adapter:outbound:objectstorage — design-decision reference

Object-storage driven adapter under dev.caskeleton.adapter.outbound.objectstorage. It implements the semantic contracts in dev.caskeleton.application.objectstorage and keeps provider keys, paths, SDK types, and control records behind the application boundary.

The allowed production dependencies are owned by src/config/architecture/modules.json. Module rules live in CLAUDE.md, the full design in docs/superpowers/specs/2026-07-28-objectstorage-production-capability-design.md, and exact evidence claims in docs/registries/object-storage-readiness.yaml.

Current readiness

Batch B implements the provider-neutral namespace/control kernel and filesystem-local-dev managed single upload/download at R1. R1 means bounded local functional evidence only:

  • immutable streamed create with exact SHA-256 and exact version inspection;
  • full/range bounded reads;
  • strict canonical control-record codecs and single-process CAS;
  • same-operation replay without invoking the producer again;
  • restart/path/symlink/corruption/fault characterization;
  • exactly-once selected-provider lifecycle.

It does not prove multi-node CAS, power-loss durability, production credentials/TLS/IAM/encryption, S3 response-loss behavior, or R2 readiness. Direct transfer, multipart, quarantine, retention, and production reconciliation cards remain R0.

Activation and routing

The canonical namespace is app.object-storage and is disabled by default. Enabling it requires an explicit provider, exact provider version, logical destination, route token, binding/policy revisions, and bounded sizes. Settings compile fully before any selected provider creates a directory, client, thread, scheduler, or credential lookup.

filesystem-local-dev is rejected under prod/production. Normal composition contributes only managed publication, inspection/transfer, retirement, and operation-resolution ports. Scanner and privileged purge composition remain separate and empty; no direct-grant port is registered.

The local provider maps provider-neutral logical control keys to .record leaf files. This permits object-store-valid pairs such as reference and reference/lifecycle without a filesystem file/directory collision; callers and durable control records retain the canonical logical keys.

Legacy compatibility

ObjectStoragePort, StoredObject, and the old whole-byte[] filesystem/S3 adapters are deprecated compatibility only. They preserve overwrite/raw-key semantics in an isolated namespace and never back the new semantic ports.

  • Preferred temporary activation: app.object-storage.legacy.enabled=true with an explicit backend and namespace.
  • Old ca-skeleton.objectstorage.* properties remain an explicit legacy-only alias.
  • Mixing any old alias with canonical settings fails startup without echoing values.
  • The sample local/test profiles explicitly opt into legacy filesystem storage until its consumer migration; no production profile receives a local fallback.

Provider and dependency scope

The AWS SDK v2 BOM stays module-scoped because Spring Boot does not manage software.amazon.awssdk:*. The existing legacy S3/MinIO adapter remains characterized but is not a canonical production provider and advances no S3 readiness card.

Verification

Focused contracts cover codecs/state machines, local provider behavior, fail-closed binding, composition side effects/lifecycle, routing replay/read behavior, and the readiness registry.

cd src
./gradlew :adapter:outbound:objectstorage:check --console=plain