2.0 KiB
2.0 KiB
Internal: Storage
Purpose
Explain the object-store interface and S3 implementation used by Narratio. Remote key layout and lifecycle belong in Operations, while operator-selected storage fields and credential mechanisms belong in Configuration.
Primary Contract
storage.ObjectStore interface:
List(ctx, prefix)Read(ctx, key)returns an object body and the generation observed with itDownload(ctx, key, localPath)Upload(ctx, localPath, key, opts)UploadConditional(ctx, source, key, opts, condition)Exists(ctx, key)
Key invariant:
- callers pass full bucket-relative keys;
- storage implementations do not infer campaign/session/run prefixes.
Composition
NewObjectStoreFromConfig constructs the S3-backed implementation from
resolved configuration. The application loads configured filesystem secrets
before calling it. The storage adapter consumes already-resolved values; it does
not own discovery, defaults, or configuration validation.
S3 Backend Behavior
- normalizes object keys.
Listpaginates and returns normalizedObjectInfo.- A truncated S3 listing must supply a new, non-empty continuation token; otherwise listing fails with bucket and prefix context instead of looping.
Downloadwrites local files with parent directory creation.Uploadstreams local file and returns remote metadata.Readbinds a returned body to its S3 ETag.UploadConditionalmaps an ETag match or absence precondition directly to the provider request and reports a failed precondition without performing a local check-then-write replacement.Existsmaps not-found responses tofalse.
Invariants
- storage layer is stateless regarding manifest/stage progression.
- publish ordering semantics are owned by stage/app code, not storage adapters.
Implementation And Tests
- Contract and S3 adapter:
internal/adapters/storage - Composition:
internal/app/object_store.go - Tests:
internal/adapters/storage/*_test.go,internal/app/object_store_test.go