Skip to content

fix: re-land CIP-002 — stranded by the #20 squash-merge - #24

Merged
ralyodio merged 1 commit into
masterfrom
fix/recover-cip-002
Aug 29, 2026
Merged

fix: re-land CIP-002 — stranded by the #20 squash-merge#24
ralyodio merged 1 commit into
masterfrom
fix/recover-cip-002

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Re-lands the CIP-002 implementation, which never reached master despite #21 showing MERGED.

What happened

#20 (feat/storage-cipsmaster) was squash-merged first, capturing the branch as it stood at that moment — the docs commit only. #21 then merged into feat/storage-cips afterwards. GitHub marks #21 merged because it reached its base branch; that base never reached master again.

Result: master's tip is the #20 squash (ed85c93), which contains docs/prds/ but none of the code. Two commits were stranded on feat/storage-cips:

917d001 feat(storage): CIP-002 storage HTTP API + `c0mpute storage` CLI (#21)
a949ae2 docs: add CIPs (content already on master via the #20 squash)

Confirmed by file: node/crates/c0mpute-store/src/tier.rs and node/crates/c0mpute-gateway/src/storage_api.rs do not exist on master.

The fix

This branch is origin/master plus a cherry-pick of 917d001 — nothing else. It re-lands exactly the change you already reviewed and approved in #21, with no content changes.

Only the CIP-002 commit is replanted. a949ae2 is deliberately left behind: its content is already on master inside the #20 squash, so replaying it would apply the docs on top of themselves.

Do not merge feat/storage-cips into master instead — that would do exactly that.

Verified

Follow-up

PR #23 (CIP-003) was stacked on feat/cip-002-storage-api, which is also off-master. It is superseded by a branch rebuilt on top of this one; #23 is closed with a pointer rather than force-pushed.

Once this merges, feat/storage-cips, feat/cip-002-storage-api and feat/cip-003-placement are all safe to delete.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LsQAuvXkmyHTgnvquLHrRx

Implements CIP-002: turns the c0mpute-store engine into a usable service and
puts it behind a CLI. Single node — every shard lands on the local disk and
host_hint stays None; cross-node placement is CIP-003.

c0mpute-store
  - Tier (hot / standard / critical) carrying (k, parity) and CIP-001 pricing.
    hot is RS k=1, which makes parity shards byte-identical copies: real 3-copy
    replication with 1x repair amplification.
  - Block layer: objects split into blocks, each independently RS-encoded, so
    memory is bounded by block size and a range read touches only the blocks it
    needs. CIP-007's random-access files depend on this.
  - Manifest v2 (version, block_size, tier, blocks[]) with a shim that still
    parses v1.
  - block_size_for() scales 4 MiB -> 256 MiB with object size, keeping a 1 TiB
    object's manifest under a megabyte instead of ~262k blocks.
  - put_stream / read_stream / get_range. put is now a wrapper over put_stream
    so there is one write path.
  - Storage::list() for enumerating what a node holds.

c0mpute-gateway
  - /storage/v1/{objects,shards,manifests,status} with Range support, tier
    selection, idempotent PUT, single-flight per hash, and a disk budget.
  - Commit-then-verify on every write: bytes must hash to the hash the caller
    committed to, or 422.
  - ed25519 signed-request auth (DIP-0007 shape) bound to method + path + body
    hash, with a 5-minute skew window. Writes require it; reads do not, since
    the hash is the capability.

c0mpute-cli
  - `c0mpute storage put|get|ls|info|verify|rm|status|tiers|serve`, per
    plugins/storage/module.toml. Commands that need later CIPs (volume, mount,
    provide) are absent rather than stubbed, and --help says which CIP brings
    each one.

Two bugs found by running it, not by the tests:

1. Data loss on rollback. A write that fails its hash commitment used to delete
   every shard hash it touched. Shards are content-addressed and shared, so
   re-uploading an existing object's bytes under a wrong hash produces the same
   shard hashes — the rollback deleted the intact object's shards. One
   malformed request destroyed real data. ChunkStore::put_new now reports
   whether it created a chunk, and rollback only removes what it created.
   Regression tests at both the store and HTTP level.

2. Tracing wrote to stdout, so `HASH=$(c0mpute storage put f)` captured log
   lines and every scripted use broke. Diagnostics now go to stderr; daemon
   mode is unaffected because it points both at the same log file.

Also: Config's api/storage/gateway sections get serde defaults, so a partial
config.toml that sets only [storage] root loads instead of erroring.

65 new tests (31 store, 12 auth, 22 HTTP integration); workspace is green and
clippy-clean on the touched crates. Verified end to end against a running
server: 12 MiB round-trips byte-identical, range reads match dd, and an object
still reconstructs after deleting 4 of 14 shards.


Claude-Session: https://claude.ai/code/session_01LsQAuvXkmyHTgnvquLHrRx

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

8 finding(s)

HIGH/CRITICAL: 5 | MEDIUM: 3

Severity Rule Location
HIGH sh-remote-script-execution scripts/dev-setup.sh:25
HIGH sh-remote-script-execution scripts/install.sh:159
HIGH sh-remote-script-execution scripts/install.sh:184
HIGH sh-remote-script-execution scripts/install.sh:277
HIGH sh-remote-script-execution scripts/install.sh:294
MEDIUM js-unescaped-html-sink apps/web/src/app/blog/[slug]/page.tsx:53
MEDIUM js-unescaped-html-sink apps/web/src/app/layout.tsx:89
MEDIUM sh-eval-expansion scripts/dev-setup.sh:35

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 242c4fa into master Aug 29, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant