Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
1edffd4
WIP add replication deps
cowlicks Feb 21, 2026
6557170
Add error from hc proto for replication
cowlicks Feb 21, 2026
159e380
Add peers to core
cowlicks Feb 22, 2026
90646e0
We're in 2024 now
cowlicks Feb 23, 2026
d0ecc05
WIP revisit after refactoring await all the `&mut self`
cowlicks Feb 23, 2026
9c9fea0
WIP update RandomAccess.len
cowlicks Feb 24, 2026
f008b01
rm async_std
cowlicks Feb 24, 2026
b1fc656
Add get_random_access
cowlicks Feb 24, 2026
997bc4e
RMME
cowlicks Feb 24, 2026
f59da0f
lints
cowlicks Feb 24, 2026
e1d1600
rm async-std
cowlicks Feb 24, 2026
805ef1b
fix the needless_pas_by_ref_mut
cowlicks Feb 24, 2026
a79047d
unnest
cowlicks Feb 24, 2026
ddfeeba
ignore for now
cowlicks Feb 24, 2026
14fdd8b
Add From<RamError> for HypercoreError
cowlicks Feb 25, 2026
be5fbdd
Add storage test
cowlicks Feb 25, 2026
fa282a3
Make flush_infos take Vec not slice
cowlicks Feb 25, 2026
659d6a2
Make Storage returnc owned futures
cowlicks Feb 25, 2026
3976700
Split core into two parts
cowlicks Feb 26, 2026
fb0a180
impl create_valueless_proof on Inner2
cowlicks Feb 27, 2026
f5253a3
add Inner2.verify_proof
cowlicks Mar 2, 2026
0757cc3
Add Inner2.byte_range
cowlicks Mar 2, 2026
9423cc5
Start using owned futures
cowlicks Mar 3, 2026
d16687f
add lint for unused async
cowlicks Mar 3, 2026
a0c3e4c
Fix mutex issues
cowlicks Mar 3, 2026
cf81436
implement Hypercore.get as owned future
cowlicks Mar 5, 2026
21df1cd
rewrite create_proof to return an owned future
cowlicks Mar 5, 2026
42f57e8
rewrite verify_and_apply_proof to return owned futures
cowlicks Mar 5, 2026
a596656
WIP add replication
cowlicks Mar 8, 2026
b709811
add tests, fix bug
cowlicks Mar 11, 2026
425a7c9
clippy --fix && cargo fmt
cowlicks Mar 11, 2026
657118d
fix lints that wouldn't auto-fix
cowlicks Mar 11, 2026
4d9315f
Remove futures replaced by owned futures
cowlicks Mar 11, 2026
081d098
Create master replicator
cowlicks Mar 14, 2026
4c15b8d
polling hypercore.get should drive replication
cowlicks Mar 16, 2026
9f5a613
use 'x.x.x-alpha' releases of random-access-* crates
cowlicks Mar 17, 2026
beb7780
remove async_std
cowlicks Mar 18, 2026
922b684
bump hypercore_handshake
cowlicks May 18, 2026
667393f
More replication tests
cowlicks May 18, 2026
78fcba7
Add more attach_replicator tests
cowlicks May 19, 2026
1fb9c99
no more "tokio" feature
cowlicks Jun 1, 2026
f27f432
rm unused features from CI
cowlicks Jun 1, 2026
a137346
Remove shared core.
cowlicks Jun 1, 2026
41e1913
More thorough CI.
cowlicks Jun 1, 2026
cac75da
use versioned hypercore-protocol
cowlicks Jun 19, 2026
067b1d4
rm more tokio feature flag stuff
cowlicks Jun 19, 2026
d244259
append/append_batch take &self
cowlicks Jul 6, 2026
9ed6552
fix needless muts from append/append_batch change
cowlicks Jul 6, 2026
79c0ae2
Make Hypercore Clone
cowlicks Jul 7, 2026
cea4bba
.claude >> .gitignore
cowlicks Jul 7, 2026
fb571d4
Add CLAUDE.md
cowlicks Jul 7, 2026
c55d756
Add Hypercore::attach_channel for multiplexed replication
cowlicks Jul 7, 2026
56a101a
Use hc proto 0.7.2 from crates.io
cowlicks Aug 7, 2026
f1870cf
update CHANGELOG.md
cowlicks Aug 7, 2026
e90c8dc
rm more async-std refs
cowlicks Aug 7, 2026
8c9a7be
add docs to gitignore
cowlicks Aug 7, 2026
a6c43e1
chore: Release hypercore version 0.17.0-alpha.1
cowlicks Aug 7, 2026
325e4d6
feat: add Hypercore::update to fetch the newest state from peers
cowlicks Aug 11, 2026
7e17984
test: cover the multi-peer upgrade-availability logic
cowlicks Aug 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 42 additions & 63 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,18 @@ jobs:
components: rustfmt
- name: Run tests
run: |
cargo check --all-targets --no-default-features --features tokio
cargo check --all-targets --no-default-features --features tokio,sparse
cargo check --all-targets --no-default-features --features tokio,sparse,cache
cargo check --all-targets --no-default-features --features async-std
cargo check --all-targets --no-default-features --features async-std,sparse
cargo check --all-targets --no-default-features --features async-std,sparse,cache
cargo test --no-default-features --features js_interop_tests,tokio
cargo test --no-default-features --features js_interop_tests,tokio,shared-core
cargo test --no-default-features --features js_interop_tests,tokio,sparse
cargo test --no-default-features --features js_interop_tests,tokio,sparse,cache
cargo test --no-default-features --features js_interop_tests,async-std
cargo test --no-default-features --features js_interop_tests,async-std,shared-core
cargo test --no-default-features --features js_interop_tests,async-std,sparse
cargo test --no-default-features --features js_interop_tests,async-std,sparse,cache
cargo test --benches --no-default-features --features tokio
cargo test --benches --no-default-features --features async-std

cargo check --all-targets
cargo check --all-targets --all-features
cargo check --all-targets --no-default-features
cargo check --all-targets --no-default-features --features sparse
cargo check --all-targets --no-default-features --features sparse,cache
cargo test
cargo test --all-features
cargo test --no-default-features
cargo test --no-default-features --features js_interop_tests
cargo test --no-default-features --features js_interop_tests,sparse
cargo test --no-default-features --features js_interop_tests,sparse,cache
cargo test --benches --no-default-features
test-windows:
runs-on: windows-latest

Expand All @@ -61,22 +56,17 @@ jobs:
components: rustfmt
- name: Run tests
run: |
cargo check --all-targets --no-default-features --features tokio
cargo check --all-targets --no-default-features --features tokio,sparse
cargo check --all-targets --no-default-features --features tokio,sparse,cache
cargo check --all-targets --no-default-features --features async-std
cargo check --all-targets --no-default-features --features async-std,sparse
cargo check --all-targets --no-default-features --features async-std,sparse,cache
cargo test --no-default-features --features tokio
cargo test --no-default-features --features tokio,shared-core
cargo test --no-default-features --features tokio,sparse
cargo test --no-default-features --features tokio,sparse,cache
cargo test --no-default-features --features async-std
cargo test --no-default-features --features async-std,shared-core
cargo test --no-default-features --features async-std,sparse
cargo test --no-default-features --features async-std,sparse,cache
cargo test --benches --no-default-features --features tokio
cargo test --benches --no-default-features --features async-std
cargo check --all-targets
cargo check --all-targets --all-features
cargo check --all-targets --no-default-features
cargo check --all-targets --no-default-features --features sparse
cargo check --all-targets --no-default-features --features sparse,cache
cargo test
cargo test --all-features
cargo test --no-default-features
cargo test --no-default-features --features sparse
cargo test --no-default-features --features sparse,cache
cargo test --benches --no-default-features

test-macos:
runs-on: macos-latest
Expand All @@ -88,22 +78,18 @@ jobs:
components: rustfmt
- name: Run tests
run: |
cargo check --all-targets --no-default-features --features tokio
cargo check --all-targets --no-default-features --features tokio,sparse
cargo check --all-targets --no-default-features --features tokio,sparse,cache
cargo check --all-targets --no-default-features --features async-std
cargo check --all-targets --no-default-features --features async-std,sparse
cargo check --all-targets --no-default-features --features async-std,sparse,cache
cargo test --no-default-features --features js_interop_tests,tokio
cargo test --no-default-features --features js_interop_tests,tokio,shared-core
cargo test --no-default-features --features js_interop_tests,tokio,sparse
cargo test --no-default-features --features js_interop_tests,tokio,sparse,cache
cargo test --no-default-features --features js_interop_tests,async-std
cargo test --no-default-features --features js_interop_tests,async-std,shared-core
cargo test --no-default-features --features js_interop_tests,async-std,sparse
cargo test --no-default-features --features js_interop_tests,async-std,sparse,cache
cargo test --benches --no-default-features --features tokio
cargo test --benches --no-default-features --features async-std
cargo check --all-targets
cargo check --all-targets --all-features
cargo check --all-targets --no-default-features
cargo check --all-targets --no-default-features --features sparse
cargo check --all-targets --no-default-features --features sparse,cache
cargo test
cargo test --all-features
cargo test --no-default-features
cargo test --no-default-features --features js_interop_tests
cargo test --no-default-features --features js_interop_tests,sparse
cargo test --no-default-features --features js_interop_tests,sparse,cache
cargo test --benches --no-default-features

build-extra:
runs-on: ubuntu-latest
Expand All @@ -115,24 +101,17 @@ jobs:
targets: wasm32-unknown-unknown
- name: Build WASM
run: |
cargo build --target=wasm32-unknown-unknown --no-default-features --features tokio
cargo build --target=wasm32-unknown-unknown --no-default-features --features async-std
cargo build --target=wasm32-unknown-unknown --no-default-features
- name: Build release
run: |
cargo build --release --no-default-features --features tokio
cargo build --release --no-default-features --features tokio,sparse
cargo build --release --no-default-features --features tokio,sparse,cache
cargo build --release --no-default-features --features async-std
cargo build --release --no-default-features --features async-std,sparse
cargo build --release --no-default-features --features async-std,sparse,cache
cargo build --release --no-default-features
cargo build --release --no-default-features --features sparse
cargo build --release --no-default-features --features sparse,cache
- name: Run examples
run: |
cargo run --no-default-features --features tokio --example disk
cargo run --no-default-features --features async-std --example disk
cargo run --no-default-features --features tokio --example memory
cargo run --no-default-features --features async-std --example memory
cargo run --no-default-features --features tokio --example replication
cargo run --no-default-features --features async-std --example replication
cargo run --no-default-features --example disk
cargo run --no-default-features --example memory
cargo run --no-default-features --example replication

lint:
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ my-first-dataset/
feed.db/
.vscode
tests/js/work
.claude
docs
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* Replication over the Hypercore wire protocol, behind the (default) `replication` feature:
* `Hypercore::replicate` builds a `Replicator` driving a Noise-encrypted connection, and
`Hypercore::replicator` builds an unconnected one to attach a connection to later.
* `Replicator::with_connection` and `Replicator::with_connection_stream` attach a transport.
* `Hypercore::attach_replicator` attaches a `Replicator` to the core.
* `Hypercore::attach_channel` joins a channel on an already-multiplexed `hypercore-protocol`
connection, for hosting several cores over one stream.
* Polling `Hypercore::get` drives replication, so a request for a block the core does not have
is served from peers.
* `HypercoreError::Protocol`, wrapping `hypercore_protocol::Error`.
* `From<RandomAccessError> for HypercoreError`.

### Changed

* `Hypercore` is now `Clone`, sharing one underlying core between clones.
* `append`, `append_batch`, `get`, `create_proof`, `verify_and_apply_proof`, `missing_nodes` and
`missing_nodes_from_merkle_tree_index` take `&self` rather than `&mut self`.
* `key_pair` returns `PartialKeypair` by value instead of `&PartialKeypair`.
* `verify_and_apply_proof` takes `Proof` by value instead of `&Proof`.
* `StorageTraits` now requires `Send + Sync`.
* Internals were reworked to produce owned (`'static`) futures rather than futures borrowing the
core, which is what lets replication hold work in flight across calls.
* `random-access-storage` bumped to `6.0.0-alpha`, `random-access-memory` and `random-access-disk`
to `4.0.0-alpha`.
* The `replication` feature now also pulls in `hypercore-protocol` and `hypercore_handshake`.
* `src/core.rs` was split into `src/core/mod.rs` and `src/core/inner.rs`.

### Removed

* The `tokio` and `async-std` features. `async-std` is no longer supported and tokio is used
unconditionally, so there is no longer a runtime to select.
* The `shared-core` feature and its `SharedCore` type. `Hypercore` is now `Clone` and its methods
take `&self`, so wrapping it in an `Arc<Mutex<_>>` is no longer needed.



## [0.16.0] - 2026-02-18
Expand Down
123 changes: 123 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

This is the **hypercore** crate, a Rust implementation of the Hypercore protocol - a secure, distributed append-only log. It aims for binary compatibility with the Javascript LTS version for disk storage while maintaining interoperability with the wire protocol.

Key characteristics:
- **100% Safe Rust**: Uses `#![forbid(unsafe_code)]`
- **Structured concurrency**: Avoids `tokio::spawn`; work only happens when polled

## JavaScript implementation

The original JavaScript implementation of hypercore is located in `/home/blake/git/hyper/js/hypercore/.`.

## Building and Testing

### Basic commands
```bash
# Run tests
cargo test

# Run linting (note: strict lints enabled)
cargo clippy
```

### Running specific tests
```bash
# Run a single test
cargo test test_name

# Run tests with specific runtime
cargo test --no-default-features --features tokio
cargo test --no-default-features --features async-std
```

### JavaScript interoperability tests
To verify compatibility with the original Javascript implementation:
```bash
cargo test --features js_interop_tests
```

These tests use the `rusty_nodejs_repl` crate to run Javascript code side-by-side with Rust, verifying that both implementations produce identical on-disk results.

### Running examples
```bash
cargo run --example memory
cargo run --example disk
cargo run --example replication
```

## Architecture

## Style guide

Favor reduced nesting wherever possible.

### Core Data Structures

The `Hypercore` struct is the main entry point, built using `HypercoreBuilder`. Internally, it consists of:

- **Oplog** (`oplog/`): Append-only operation log with dual-header structure for crash recovery. Headers alternate between two memory areas for atomic updates. Stores entries with CRC checksums.

- **MerkleTree** (`tree/`): Sparse merkle tree storing cryptographic hashes. Uses root indices to track tree state. Supports optional Moka cache for node lookups (with `cache` feature).

- **BlockStore** (`data/`): Stores actual data blocks indexed by position.

- **Bitfield** (`bitfield/`): Tracks which blocks are available locally (for sparse replication). Has `fixed` and `dynamic` implementations.

- **Storage** (`storage/`): Abstraction over storage backends (memory/disk). Splits data into four stores: `tree`, `data`, `bitfield`, and `oplog`. Non-WASM targets use `random-access-disk`; WASM uses `random-access-memory`.

### Replication

The `replication/` module (enabled with `replication` feature, on by default) handles:

- **Events** (`events.rs`): Broadcast events to peers using `async-broadcast`
- **Peer**: Represents remote peers with protocol state

Replication works by creating proofs in a source hypercore and verifying/applying them to destination hypercores.

### Key Modules

- **crypto/**: Cryptographic operations (signing, verification, hashing) using ed25519-dalek and blake2
- **encoding.rs**: Compact binary encoding utilities
- **builder.rs**: Fluent API for constructing Hypercore instances
- **common/**: Shared types including error types, store definitions, and internal utilities

## Features

Default features: `sparse`, `replication`, `cache`

- `tokio` / `async-std`: Choose async runtime (mutually exclusive, at least one required)
- `sparse`: Enables sparse file support for disk storage
- `replication`: Enables replication support and event broadcasting
- `cache`: Moka-based cache for merkle tree nodes
- `js_interop_tests`: Enables Javascript interoperability tests (dev-only)

## Code Style and Lints

This crate has strict linting enabled (see `.clippy.toml` and `lib.rs`):
- `#![forbid(unsafe_code)]` - no unsafe code allowed
- `#![cfg_attr(test, deny(warnings))]` - warnings become errors in tests
- Additional lints: `unreachable_pub`, `redundant_lifetimes`, `clippy::needless_pass_by_value`, etc.

All public APIs must have documentation (`missing_docs` warning).

## Testing Strategy

Tests are organized in:
- `tests/core.rs`: Core functionality tests
- `tests/model.rs`: Property-based tests using `proptest`
- `tests/js_interop.rs`: Javascript interoperability verification
- `tests/common/`: Shared test utilities
- `tests/js/`: Javascript test harness code

## Important Implementation Details

- **Header structure**: Uses dual-header approach where headers alternate between two 4KB regions for atomic updates
- **Oplog entries**: Each entry has an 8-byte "leader" (4-byte CRC + 4-byte length/flags)
- **Storage flush**: Uses `skip_flush_count` for batching writes (mirrors JS `autoFlush`)
- **Node indexing**: Uses flat-tree indices for merkle tree navigation
- **Crash recovery**: Oplog design ensures consistency even if process crashes during write
42 changes: 29 additions & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "hypercore"
version = "0.16.0"
version = "0.17.0-alpha.1"
license = "MIT OR Apache-2.0"
description = "Secure, distributed, append-only log"
documentation = "https://docs.rs/hypercore"
Expand Down Expand Up @@ -32,21 +32,39 @@ flat-tree = "6"
merkle-tree-stream = "0.12"
pretty-hash = "0.4"
rand = "0.8"
random-access-memory = "3"
random-access-storage = "5"
sha2 = "0.10"
futures = "0.3"
crc32fast = "1"
intmap = "2"
moka = { version = "0.12", optional = true, features = ["sync"] }
async-broadcast = { version = "0.7.1", optional = true }
async-lock = {version = "3.4.0", optional = true }
futures-lite = "2.6.1"

[dependencies.hypercore_schema]
version = "0.2.0"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
random-access-disk = { version = "3", default-features = false }
[dependencies.hypercore-protocol]
version = "0.7.2"
optional = true

[dependencies.hypercore_handshake]
version = "0.6.0"
optional = true

[dependencies.tokio]
version = "1"
default-features = false
features = ["time"]

[dependencies.random-access-storage]
version = "6.0.0-alpha"

[dependencies.random-access-memory]
version = "4.0.0-alpha"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies.random-access-disk]
version = "4.0.0-alpha"
default-features = false

[dev-dependencies]
anyhow = "1.0.70"
Expand All @@ -55,21 +73,19 @@ proptest-derive = "0.5.1"
data-encoding = "2.2.0"
remove_dir_all = "0.7.0"
tempfile = "3.1.0"
async-std = { version = "1.12.0", features = ["attributes"] }
tokio = { version = "1.27.0", default-features = false, features = ["macros", "rt", "rt-multi-thread"] }
tokio = { version = "1.27.0", default-features = false, features = ["macros", "rt", "rt-multi-thread", "io-util", "time"] }
tokio-util = { version = "0.7", features = ["compat"] }
uint24le_framing = { version = "0.2.0" }
tokio-test = "0.4"
sha2 = "0.10"
criterion = { version = "0.4", features = ["async_std", "async_tokio"] }
test-log = { version = "0.2.11", default-features = false, features = ["trace"] }
tracing-subscriber = { version = "0.3.16", features = ["env-filter", "fmt"] }

[features]
default = ["tokio", "sparse", "replication", "cache"]
replication = ["dep:async-broadcast"]
shared-core = ["replication", "dep:async-lock"]
default = ["sparse", "replication", "cache"]
replication = ["dep:async-broadcast", "dep:hypercore-protocol", "dep:hypercore_handshake"]
sparse = ["random-access-disk/sparse"]
tokio = ["random-access-disk/tokio"]
async-std = ["random-access-disk/async-std"]
cache = ["moka"]
# Used only in interoperability tests under tests/js-interop which use the javascript version of hypercore
# to verify that this crate works. To run them, use:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ corresponding wire protocol implementation.
- [x] Get entries from hypercore
- [x] Clear range from hypercore, with optional support for sparse files
- [x] Support basic replication by creating proofs in a source hypercore and verifying and applying them to a destination hypercore
- [x] Support `tokio` or `async-std` runtimes
- [x] ~~Support `tokio` or `async-std` runtimes~~ `async-std` support dropped `tokio` used unconditionally
- [x] Support WASM for in-memory storage
- [x] Test Javascript interoperability for supported features
- [x] Add optional read cache
Expand Down
Loading
Loading