You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The blob-removal RFC drops the space from the client-leg arguments —
/blob/remove is {digest} and /blob/abort is {digest, cause}, with the
space as the invocation subject. Ingot already invoked with the space as
subject, so only the argument literals change.
Treat the node's BlobAccepted refusal on abort as final rather than a
fault: it means this space accepted the same content concurrently, so
the blob belongs to the reference index and is released via
/blob/remove when its last claim drops. The uploader logs it distinctly
instead of as an error; the multipart cleanup path already discards the
park row, which is obsolete either way.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| `allocate` / `PUT` / `accept` blob lifecycle | Piri/Sprue | **exists** | The storage primitive Ingot builds on. |
527
528
| Ingot-timed accept (PUT a part, defer the conclude until Complete) | Ingot + Sprue | **exists** | `forgeclient.BlobAddParked`/`BlobConclude` split the flow at the conclude seam; UploadPart parks (durable, unaggregated), Complete concludes — Sprue's conclude handler already ran accept standalone. Ingot still does **not** issue `accept` (Piri requires the upload-service DID). |
528
-
|`unallocate(digest)` — drop a parked blob | Piri + Sprue + libforge |**exists**|`/blob/unallocate` on Pirirefuses accepted blobs (`BlobAccepted`), deletes the space's allocation, and drops the bytes at zero allocations. Sprue recovers the provider from the `cause` receipt chain (a parked blob has no registration). Abort/TTL/supersede unwind through it. |
529
-
| `remove(digest)` — per-space claim release; physical delete/piece-retire at zero global claims | Piri + Sprue + libforge | **exists** | `/blob/remove` on Piri deletes the space's allocation/acceptance/claim; at zero claims bytes delete immediately (unaggregated) or via the pending-removal sweep once the whole aggregate root is dead (FIL-623/624). Sprue forwards to primary + replicas (FIL-522). |
529
+
|`abort(digest)` — drop a parked blob | Piri + Sprue + libforge |**exists**|`/blob/abort` on Sprue translates to `/blob/reject` on Piri, which refuses blobs the invoking space has accepted (`BlobAccepted`), deletes the space's allocation, and drops the bytes once no space holds an allocation or acceptance. Sprue recovers the provider from the `cause` receipt chain (a parked blob has no registration). Abort/TTL/supersede unwind through it. |
530
+
| `remove(digest)` — per-space claim release; physical delete/piece-retire at zero global claims | Piri + Sprue + libforge | **exists** | `/blob/remove` on Sprue forwards `/blob/release` to Piri, which deletes the space's allocation/acceptance/claim; at zero claims bytes delete immediately (unaggregated) or via the pending-removal sweep once the whole aggregate root is dead (FIL-623/624). Sprue forwards to primary + replicas (FIL-522). |
530
531
| Configurable, adaptive size policy (`min`/`max`); batch guard for the `extraData` cap | Piri |**partial**|`MinAggregateSize` is hardcoded 128 MiB; lower to ~8 MiB and make configurable. The `addPieces` batch is no longer contract-capped (FWSS v1.3.0 removed the `extraData` cap); size it to the FVM `PiecesAdded` event-size + per-tx gas — a measured ceiling (default `BatchSize=10` is safely within it) (pdp-sim). No contract change. |
531
532
| Compaction (Regime B) + complete the on-chain delete signature | Piri |**partial**| Whole-root delete is signed and wired (`schedulePieceDeletions` with `SignSchedulePieceRemovals` extraData); compaction (remove + re-hash survivors + re-add) is new. |
532
533
| De-dup at accept (don't re-aggregate a digest already a live piece) | Piri | **to-build** | Backstops one-piece-per-content once accept timing is Ingot-driven. |
@@ -539,7 +540,7 @@ negotiations).
539
540
540
541
**Determinism / idempotency Ingot must preserve.** The `accept` invocation is built deterministically
541
542
(stable CID, today via `WithNoNonce` over `{space, digest, size, put-task}`); re-driving accept must
542
-
reuse the same put-task link. `remove`/`unallocate` must be idempotent. The forge-root advance must
543
+
reuse the same put-task link. `remove`/`abort` must be idempotent. The forge-root advance must
543
544
happen only after a successful guarded root swap — the catalog log currently advances it before the
544
545
swap, so a mismatch can leave the forge root pointing at a bucket root that was never adopted.
545
546
@@ -592,7 +593,7 @@ The MVP this supersedes had six structural problems; each is resolved by a layer
| A per-bucket lock held across the whole write | Ingest and upload run off-lock; only the MST splice + guarded root swap is in the critical section. [§7.1](#71-write-single-shot-putobject) |
594
595
| The whole object buffered in memory | Hash-while-writing to the local store; stream to Piri; nothing held whole in RAM. [§5](#5-the-data-layer), [§7.1](#71-write-single-shot-putobject) |
| Objects chunked into CARs to obtain a digest | Object body = one blob (≤ `max`) or a coarse `≤ max` split; no fine chunking, no CAR. [§5](#5-the-data-layer), [§10](#10-deployment-topology--the-digest-before-upload-cost) |
597
598
| No delete of superseded data | Reference index + per-space `remove` + Piri's global claim gate + indexer delete; O(1) for ≥ `min` blobs. [§5](#5-the-data-layer), [§6](#6-the-forgechain-layer), [§9](#9-the-system-contract-piri--sprue--indexer) |
598
599
| Aggregation blocked partial deletes | A small `min` makes most blobs their own piece (O(1) delete); compaction handles only the sub-`min` tail. [§6](#6-the-forgechain-layer) |
@@ -801,20 +802,20 @@ reference index — and is out of scope for this iteration.
801
802
The in-memory harness uses a no-op uploader and serves reads from the spool, so these forge-network
802
803
paths are stubbed in-tree and verified against a real sprue+piri+indexer later:
803
804
804
-
- **`remove(digest)` and `unallocate(digest)` are live.**`RemoveBlob` invokes `/blob/remove` on
805
-
sprue, which forwards to the storage nodes ([§9](#9-the-system-contract-piri--sprue--indexer)); delete finality means claim-release-now,
806
-
bytes-at-root-death. `UnallocateBlob` retires parked part-blobs on abort via `/blob/unallocate`
807
-
(provider recovered from the `cause` receipt chain); allocation-expiry GC (FIL-625) remains the
808
-
backstop when an abort never arrives.
805
+
- **`remove(digest)` and `abort(digest)` are live.**`RemoveBlob` invokes `/blob/remove` on
806
+
sprue, which forwards `/blob/release`to the storage nodes ([§9](#9-the-system-contract-piri--sprue--indexer)); delete finality means claim-release-now,
807
+
bytes-at-root-death. `AbortBlob` retires parked part-blobs via `/blob/abort` — sprue translates
808
+
it into `/blob/reject` on the node (provider recovered from the `cause` receipt chain);
809
+
allocation-expiry GC (FIL-625) remains the backstop when an abort never arrives.
809
810
- **The local-table `Locator`read tier is not wired.** Body-blob *locations* are recorded at accept,
810
811
but the read path that consumes them ([§7.4](#74-read-getobject), [§8](#8-retrieval-addressing-when-bodies-need-a-sharded-dag-index)) is deferred — it is only exercised after spool
811
812
eviction (also not built) and is best validated live.
812
813
- **Multipart parts park at UploadPart, accept at Complete.** (Built: `parkBlobs`/`concludeBlobs`
813
814
over the `blob_parks` table.) The in-process harness still spools parts
814
815
at `UploadPart` and uploads+accepts them at `Complete`; the true forge *parking* (upload early,
815
-
accept-at-Complete) and `unallocate`-on-abort from [§7.2](#72-multipart)–[7.3](#73-the-session-latch-the-abortcomplete-race) are forge-mode refinements.
816
+
accept-at-Complete) and the `/blob/abort` unwind from [§7.2](#72-multipart)–[7.3](#73-the-session-latch-the-abortcomplete-race) are forge-mode refinements.
816
817
- **Crash recovery for the spool is not built.** The `upload_intents` × `blob_refs` reconciliation
817
-
the failure-mode table in [§7.5](#75-concurrency-durability-and-failure-modes) describes (resume/`unallocate` parked, `remove` accepted-but-unreferenced)
818
+
the failure-mode table in [§7.5](#75-concurrency-durability-and-failure-modes) describes (resume/`abort` parked, `remove` accepted-but-unreferenced)
818
819
is a later phase; a partial post-commit reference-index write currently relies on retry/idempotency.
819
820
- **`UploadPartCopy` and indexer retraction on delete** are unimplemented
820
821
(`ErrNotImplemented` / no-op). `ListParts` and `ListMultipartUploads` are implemented
@@ -824,7 +825,7 @@ paths are stubbed in-tree and verified against a real sprue+piri+indexer later:
824
825
sessions and committed objects), and a background sweeper aborts open sessions older
825
826
than `multipart_session_ttl` (default 7d) and reaps terminal session rows. A successful
826
827
Complete retains its session in state `completed` so a duplicate Complete is idempotent
827
-
per S3. The network-side `unallocate`-on-abort remains a parking-flow concern (above).
828
+
per S3. The network-side `/blob/abort` unwind remains a parking-flow concern (above).
0 commit comments