Files
poimen/tasks/T7.2-object-store-blobstore.md

3.8 KiB

T7.2 — Object-store BlobStore

Field Value
Phase P7 — Distribution
Size M — 1 to 3 days
Status Not started
Flags
Spec inlined below
Blocks

Goal

S3-compatible BlobStore, tenant-namespaced, passing the same conformance suite as the embedded implementation.

Facts (inlined — no spec read needed)

  • Same port as T0.7: put / get / delete, all async, all taking TenantId.
  • Blobs are namespaced per tenant even though they are content-addressed. Global deduplication across tenants is a leak: a shared blob makes one tenant's storage accounting depend on another's, and a hash becomes an oracle for "does anyone else have this content". Deduplicate within a tenant, never across.
  • delete is not optional — reduction (T8.4) and tenant deletion both require it. A store that cannot delete cannot honour either.
  • get returning None is a normal answer, not an error.
  • Cross-tenant isolation must be verified by attempting a cross-tenant fetch, not by reading the key-construction code.

Steps

  1. Implement over an object_store-style client. Key layout puts the tenant in the path prefix: {tenant}/{blake3-hash}.
  2. put: hash, then conditional put (skip if present) — dedup falls out of the content-addressed key, scoped by the tenant prefix.
  3. get: fetch, map a not-found response to Ok(None), everything else to an error.
  4. delete: delete the object. A missing object is not an error — reduction can run twice.
  5. Set retry and timeout policy on the client explicitly. A blob fetch stalling forever inside a verifier is a hang the deadline should catch, but the client should not depend on that.
  6. Run the T0.7 conformance suite unmodified, plus the explicit cross-tenant fetch attempt.

Acceptance

  • Same conformance suite as the embedded store passes.
  • Tenant isolation verified by attempting a cross-tenant fetch.

Verify

Harness: the T0.7 conformance function against MinIO or an S3-compatible container. Same suite, unmodified.

Integration testtests/it_object_store_conformance.rs:

  1. Run the T0.7 suite verbatim against the object store.
  2. Cross-tenant attempt: with tenant B's credentials/prefix, attempt to fetch tenant A's ref. Assert it fails or returns None — attempt it, do not infer isolation from the key-construction code.
  3. Assert identical content under two tenants produces two objects, verified by listing the bucket, not by the return values.
  4. Assert a 404 on get maps to Ok(None) and a 404 on delete maps to Ok(()), so reduction is idempotent.
  5. Re-hash every stored object against its key.
  6. Latency/failure behaviour: inject a slow response; assert the configured timeout fires rather than hanging a verifier indefinitely.
  7. Run the same suite against redb in the same job to catch backend divergence.

Command: cargo test -p storage-s3 --test it_object_store_conformance

False pass:

  • Step 2 performed with the same credentials for both tenants. That tests the key prefix, not isolation. Use separate scoped credentials where the deployment will.
  • Step 3 asserting on the returned refs, which are equal by design (same content hash). The bucket listing is what shows two objects.
  • Mocking the object store. The failure modes worth catching here — eventual consistency, 404 shapes, timeouts — exist only in a real implementation.

Traps

  • A flat bucket layout with the tenant in metadata rather than the key. It dedups across tenants by construction.
  • Treating a 404 on delete as a failure, which breaks idempotent reduction.

Background (not required to do this task): rust-agentic-sys.md §3, §7, §8.6 · rust-agentic-task.md