Skip to content

Commit 2b26d57

Browse files
committed
Sync trunk with main at v0.3.0
Brings trunk up to date with all work on main through v0.3.0: - S3 streaming transfers and sccache integration - SMB2 compounding, small-file fast paths, benchmarks (v0.2.0) - Non-blocking file logging with slog!/serr! macros, ISO-8601 timestamps - Version bump to 0.3.0, README updates
1 parent f964d0f commit 2b26d57

24 files changed

Lines changed: 2472 additions & 549 deletions

.github/workflows/ci.yml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,22 @@ jobs:
6868
brew install awscli
6969
fi
7070
71+
- name: Verify spiceio binary version
72+
run: |
73+
BUILT_VERSION=$(./target/debug/spiceio --version)
74+
echo "Built: ${BUILT_VERSION}"
75+
echo "SPICEIO_BUILT_VERSION=${BUILT_VERSION}" >> "$GITHUB_ENV"
76+
77+
# If an older spiceio is already listening (e.g. from a previous run),
78+
# kill it so the test starts the freshly-built binary.
79+
for PORT in 18333 18334; do
80+
STALE_PID=$(lsof -i ":${PORT}" -sTCP:LISTEN -t 2>/dev/null || true)
81+
if [[ -n "$STALE_PID" ]]; then
82+
echo "Stale process on :${PORT} (pid ${STALE_PID}), killing..."
83+
kill "$STALE_PID" 2>/dev/null || true
84+
fi
85+
done
86+
7187
- name: Run sccache integration test
7288
# Skipped when UNAS_SMB_PASS secret is not configured (e.g. fork PRs)
7389
if: ${{ env.HAS_SMB_PASS == 'true' }}
@@ -80,9 +96,6 @@ jobs:
8096
SPICEIO_REGION: ${{ vars.SPICEIO_REGION || 'us-west-1' }}
8197
run: ./scripts/test-sccache.sh
8298

83-
- name: Compile benchmarks
84-
run: cargo bench --locked --no-run
85-
8699
- name: Build release artifact
87100
run: cargo build --release --locked --bin spiceio
88101

.gitignore

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1 @@
1-
# Generated by Cargo
2-
# will have compiled files and executables
3-
debug
4-
target
5-
6-
# These are backup files generated by rustfmt
7-
**/*.rs.bk
8-
9-
# MSVC Windows builds of rustc generate these, which store debugging information
10-
*.pdb
11-
12-
# Generated by cargo mutants
13-
# Contains mutation testing data
14-
**/mutants.out*/
15-
16-
# RustRover
17-
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18-
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19-
# and can be added to the global gitignore or merged into this file. For a more nuclear
20-
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
21-
#.idea/
1+
/target

CLAUDE.md

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,9 @@ make release # optimized release build
2121
make lint # fmt-check + check + strict clippy + rustdoc warnings
2222
make test # sccache integration test (requires SPICEIO_SMB_USER/PASS)
2323
make fmt # auto-format
24-
make bench # criterion benchmarks (crypto + protocol)
25-
make bench-live # live throughput benchmarks against NAS
2624
make clean # cargo clean
2725
```
2826

29-
## Pre-PR checklist
30-
31-
Always run these before creating a PR — they mirror what CI checks:
32-
33-
```bash
34-
cargo fmt --all # auto-format first
35-
cargo fmt --all --check # verify no formatting diff remains
36-
cargo clippy --locked --all-targets --all-features -- -D warnings -D clippy::all -D clippy::cargo -A clippy::cargo-common-metadata
37-
RUSTDOCFLAGS="-D warnings" cargo doc --locked --workspace --no-deps --document-private-items
38-
cargo test --locked # unit tests must pass
39-
```
40-
41-
Or simply: `make lint && cargo test --locked`
42-
43-
The CI also runs `./scripts/test-sccache.sh` (sccache integration test) when SMB credentials are available.
44-
4527
The binary requires these environment variables:
4628
- `SPICEIO_SMB_SERVER` (required) — SMB server hostname or IP
4729
- `SPICEIO_SMB_USER` (required) — SMB username
@@ -52,6 +34,7 @@ The binary requires these environment variables:
5234
- `SPICEIO_SMB_DOMAIN` — SMB domain (default empty)
5335
- `SPICEIO_BUCKET` — virtual S3 bucket name (defaults to `SPICEIO_SMB_SHARE`)
5436
- `SPICEIO_REGION` — AWS region to advertise (default `us-east-1`)
37+
- `SPICEIO_LOG_FILE` — append logs to this file in addition to stderr (optional; non-blocking, never stalls the proxy)
5538

5639
## Architecture
5740

Cargo.lock

Lines changed: 15 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "spiceio"
3-
version = "0.1.0"
3+
version = "0.3.0"
44
edition = "2024"
55
description = "S3-compatible API proxy to SMB file shares"
66
license = "Apache-2.0"
@@ -35,6 +35,10 @@ harness = false
3535
name = "protocol_bench"
3636
harness = false
3737

38+
[[bench]]
39+
name = "s3_bench"
40+
harness = false
41+
3842
[lints.rust]
3943
warnings = "deny"
4044
unsafe_op_in_unsafe_fn = "deny"

Makefile

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: build release check fmt fmt-check clippy doc lint test bench bench-live clean all
1+
.PHONY: build release check fmt fmt-check clippy doc lint test test-extended clean all
22

33
all: fmt lint test build
44

@@ -28,11 +28,8 @@ lint: fmt-check check clippy doc
2828
test: build
2929
./scripts/test-sccache.sh
3030

31-
bench:
32-
cargo bench --locked
33-
34-
bench-live: release
35-
./scripts/bench-live.sh
31+
test-extended: build test
32+
./scripts/test-sccache-spiceai.sh
3633

3734
clean:
3835
cargo clean

README.md

Lines changed: 101 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,71 @@
11
# spiceio
22

3-
S3-compatible API proxy to SMB file shares. Translates S3 HTTP requests into SMB 3.1.x wire protocol operations over TCP.
3+
**S3-compatible API proxy that turns any SMB share into an S3 endpoint** -- no mounting, no `libsmbclient`, no FUSE. Translates S3 HTTP requests directly into SMB 3.1.x wire-protocol operations over TCP.
44

5-
- Speaks SMB directly — no mount, no libsmbclient
6-
- NTLMv2 authentication via macOS CommonCrypto (no external crypto crates)
7-
- macOS 26+ only
5+
## Why spiceio
86

9-
## Supported S3 operations
7+
Most tools that bridge SMB and S3 (MinIO, s3proxy, VersityGW) require mounting the share to the local filesystem first. spiceio skips that entirely -- it speaks the SMB wire protocol directly over TCP (port 445), so there's no mount, no kernel driver, and no FUSE layer in the way.
8+
9+
This makes it the simplest path from "I have an SMB share" to "any S3 client can use it":
10+
11+
```
12+
S3 client ---> spiceio (HTTP :8333) ---> SMB server (TCP :445)
13+
S3 API translation SMB 3.1.x wire protocol
14+
```
15+
16+
### Key highlights
17+
18+
- **Zero-mount design** -- speaks SMB 3.1.x natively over TCP, never touches the local filesystem
19+
- **Full S3 compatibility** for common operations: Get/Put/Copy/Delete/Head Object, ListObjects (v1 & v2), ListBuckets, multipart uploads, range + conditional requests
20+
- **SMB2 compounding** -- batches Create+Read+Close or Create+Write+Close into single round trips for small file performance
21+
- **Streaming I/O** -- GetObject and PutObject stream directly between HTTP and SMB without buffering entire files
22+
- **Non-blocking logging** -- timestamped stdout/stderr with optional file tee via `SPICEIO_LOG_FILE`; dedicated writer thread, never stalls the proxy
23+
- **Simple config** -- everything via environment variables, single binary, `--version` flag
24+
- **Zero external crypto** -- NTLMv2 auth and AES-CMAC signing via macOS CommonCrypto FFI
25+
26+
### Use cases
27+
28+
- **sccache remote cache** -- point sccache at spiceio to store build cache on a NAS without cloud storage
29+
- **CI artifact storage** -- use `aws s3 cp` to push/pull build artifacts from any SMB share
30+
- **NAS integration** -- give S3-native tools access to existing file shares
31+
32+
## Quick start
33+
34+
Requires macOS 26+ and Rust (edition 2024).
35+
36+
```bash
37+
make release
38+
```
39+
40+
```bash
41+
export SPICEIO_SMB_SERVER=nas.local
42+
export SPICEIO_SMB_USER=admin
43+
export SPICEIO_SMB_PASS=secret
44+
export SPICEIO_SMB_SHARE=files
45+
./target/release/spiceio
46+
```
1047

11-
GetObject (range + conditional), PutObject, CopyObject, DeleteObject, HeadObject, ListObjectsV1/V2, ListBuckets, multipart uploads (create/upload-part/complete/abort/list-parts/list-uploads), HeadBucket, GetBucketLocation, and stubs for ACL, tagging, and versioning.
48+
Now any S3 client works:
1249

13-
## Build
50+
```bash
51+
aws s3 ls s3://files/ --endpoint-url http://localhost:8333
52+
aws s3 cp myfile.txt s3://files/remote.txt --endpoint-url http://localhost:8333
53+
```
1454

15-
Requires Rust (edition 2024) and macOS 26+.
55+
### sccache example
1656

1757
```bash
18-
make # fmt + lint + test + build
19-
make release # optimized release build
20-
make test # run tests
21-
make lint # fmt-check + check + strict clippy + rustdoc warnings
58+
export SCCACHE_BUCKET=files
59+
export SCCACHE_ENDPOINT=http://localhost:8333
60+
export SCCACHE_REGION=us-east-1
61+
export SCCACHE_S3_USE_SSL=false
62+
export SCCACHE_S3_KEY_PREFIX=sccache
63+
export AWS_ACCESS_KEY_ID=test
64+
export AWS_SECRET_ACCESS_KEY=test
65+
export RUSTC_WRAPPER=sccache
66+
export CARGO_INCREMENTAL=0
67+
68+
cargo build # artifacts cached on your NAS via spiceio
2269
```
2370

2471
## Configuration
@@ -36,24 +83,56 @@ All configuration is via environment variables:
3683
| `SPICEIO_SMB_DOMAIN` | no | *(empty)* | SMB domain |
3784
| `SPICEIO_BUCKET` | no | `SPICEIO_SMB_SHARE` | Virtual S3 bucket name |
3885
| `SPICEIO_REGION` | no | `us-east-1` | AWS region to advertise |
86+
| `SPICEIO_LOG_FILE` | no | *(none)* | Append logs to file (non-blocking) |
3987

40-
## Usage
88+
## Supported S3 operations
89+
90+
- **Objects**: GetObject (range + conditional), PutObject (conditional-write), CopyObject, DeleteObject, HeadObject
91+
- **Listing**: ListObjectsV1, ListObjectsV2, ListBuckets
92+
- **Multipart**: CreateMultipartUpload, UploadPart, CompleteMultipartUpload, AbortMultipartUpload, ListParts, ListMultipartUploads
93+
- **Bucket**: HeadBucket, GetBucketLocation, CreateBucket, DeleteBucket
94+
- **Stubs**: ACL, tagging, versioning, encryption, lifecycle, CORS (returns valid empty responses)
95+
96+
Path-style addressing only (no virtual-hosted-style).
97+
98+
## Architecture
99+
100+
Three modules:
101+
102+
- **`s3`** -- HTTP layer. Parses S3 requests, produces XML responses. Router dispatches to the appropriate handler. Small files (<64KB) use compound fast paths; large files stream.
103+
- **`smb`** -- Wire protocol client. Manages TCP connection, negotiate/session-setup handshake, and file operations. Supports SMB2 compounding for batching multiple operations in a single round trip.
104+
- **`crypto`** -- FFI bindings to macOS CommonCrypto. MD4, SHA-256, SHA-512, HMAC-MD5, HMAC-SHA256, AES-128-CMAC. No Rust crypto crates.
41105

42-
```bash
43-
export SPICEIO_SMB_SERVER=nas.local
44-
export SPICEIO_SMB_USER=admin
45-
export SPICEIO_SMB_PASS=secret
46-
export SPICEIO_SMB_SHARE=files
47-
./target/release/spiceio
106+
```
107+
HTTP request
108+
-> s3::router::handle_request
109+
-> smb::ops::ShareSession method
110+
-> smb::client::SmbClient wire operations
111+
-> TCP to SMB server
48112
```
49113

50-
Then use any S3 client pointed at `http://localhost:8333`:
114+
## Development
51115

52116
```bash
53-
aws s3 ls s3://files/ --endpoint-url http://localhost:8333
54-
aws s3 cp local.txt s3://files/remote.txt --endpoint-url http://localhost:8333
117+
make # fmt + lint + test + build
118+
make release # optimized release build
119+
make lint # fmt-check + check + strict clippy + rustdoc warnings
120+
make test # S3 API tests + sccache integration test
121+
make test-extended # above + builds spiceai repo through sccache/spiceio
122+
make clean # cargo clean
55123
```
56124

125+
Tests require `SPICEIO_SMB_USER` and `SPICEIO_SMB_PASS` environment variables and access to an SMB server.
126+
127+
## How it compares
128+
129+
| Tool | Needs local mount? | SMB access method | Cross-platform? | Best for |
130+
|---|---|---|---|---|
131+
| **spiceio** | No | Direct SMB 3.1.x wire | macOS 26+ only | Cleanest wire-level proxy, zero dependencies |
132+
| **rclone** | No | rclone's SMB backend | Yes | Cross-platform, battle-tested |
133+
| MinIO + mount | Yes | CIFS/FUSE mount | Yes | Production-grade S3 features |
134+
| s3proxy / VersityGW | Yes | CIFS/FUSE mount | Yes | Lightweight or high-perf FS backends |
135+
57136
## License
58137

59138
Apache 2.0

0 commit comments

Comments
 (0)