S3-Compatible Object Storage — Enterprise-Grade, Self-Deployable, .NET-Powered.
Means is a self-deployable S3-compatible object storage service built on ASP.NET Core. It uses the custom XlFs storage backend (inspired by MinIO's architecture — single-node multi-disk, object manifest, quorum-based writes, LogDb metadata indexing), powered entirely by the self-developed MeansLogDb metadata engine.
As of 2026-05-09: This document reflects the current code state of the repository, not aspirational design goals.
- Architecture Overview
- Implemented Capabilities
- Not Yet Implemented
- Quick Start
- Configuration Reference
- Development & Testing
- SDKs & Specification
- Project Map
- License
| Layer | Project | Purpose |
|---|---|---|
| Host / API | src/Means | ASP.NET Core net10.0 application entry point, middleware pipeline, endpoints, DI composition |
| Protocol | src/Means.Protocol.S3 | S3-compatible address resolution, SigV4 signature validation, S3 XML response serialization |
| Storage Engine | src/Means.Infrastructure.XlFs | Default XlFs storage backend (multi-disk, manifest, quorum, MeansLogDb metadata) |
| Core Abstractions | src/Means.Core | Domain models, storage interfaces, policies, error definitions, placement strategies |
| Management | src/Means/Endpoints/Console | Cookie-authenticated Console JSON API |
| Web UI | src/Means/wwwroot | React-based management dashboard (built from web/) |
| SDK - C# | SDKs/csharp | Full-featured C# client SDK |
| SDK - TypeScript | SDKs/typescript | Browser-safe TS SDK + Node extension (SigV4) |
| Tests | tests/ | Unit, integration, and contract tests |
Means.slnx
├── src/
│ ├── Means/ # Host application
│ ├── Means.Core/ # Core abstractions & domain
│ ├── Means.Infrastructure.XlFs/ # XlFs storage engine
│ └── Means.Protocol.S3/ # S3 protocol implementation
│
├── tests/
│ ├── Means.UnitTests/ # Unit tests
│ ├── Means.IntegrationTests/ # Integration tests
│ └── Means.ContractTests/ # SDK spec compliance tests
│
├── SDKs/
│ ├── csharp/ # C# SDK
│ ├── typescript/ # TypeScript SDK
│ └── spec/ # Machine-readable protocol spec
│
├── web/ # React frontend (Vite)
└── docs/ # Documentation site
| Category | Operations | Status |
|---|---|---|
| Service | ListBuckets | ✅ |
| Bucket | CreateBucket, HeadBucket, DeleteBucket | ✅ |
| Object | PutObject, GetObject, HeadObject, DeleteObject | ✅ |
| Listing | ListObjectsV2 (prefix, delimiter, continuation-token, max-keys) | ✅ |
| Copy | CopyObject (x-amz-copy-source, COPY/REPLACE metadata directive) | ✅ |
| Multipart | Initiate, UploadPart, UploadPartCopy, Complete, Abort, ListParts, ListMultipartUploads | ✅ |
| Versioning | ?versioning, ?versions, GET/HEAD/DELETE by versionId, delete markers | ✅ |
| Tagging | ?tagging (current & specific version) | ✅ |
| Lifecycle | ?lifecycle (expiration, noncurrent cleanup, AbortIncompleteMultipartUpload) | ✅ |
| CORS | ?cors config CRUD, OPTIONS preflight | ✅ |
| Notification | ?notification config persistence (reserved interface) | ✅ |
| Policy | ?policy sub-resource (GET/PUT/DELETE) | ✅ |
| Pre-signed URL | SigV4 pre-signed GET, PUT, multipart UploadPart | ✅ |
- Address Resolution: Supports both
path-styleandvirtual-hosted-styleS3 addressing. - Response Format: Unified S3-compatible XML (listings, errors, copy results).
- Range Reads:
Rangeheader →206 Partial Content; invalid ranges →416 InvalidRangewithContent-Rangeheader. - Compression: Content-negotiated (
br/gzip) on responses; disabled for Range requests. - Atomic Writes:
PutObjectis atomic — objects become visible only after the metadata transaction commits. - Multipart Rules:
partNumber:1–10000- Minimum part size (except last):
5 MiB - Final ETag:
md5(concat(part-md5-bytes))-part-count Means:RequestLimits:MaxUploadSizeBytesapplies per-part, not total assembled size.
- Concurrency Control: S3
PUTand multipartUploadPartshare a global concurrency limit (default: 64). Exceeding returns503 SlowDownwithRetry-After: 1. - Checksum Verification: Disabled by default on reads to avoid double I/O for large objects. Enable via
VerifyChecksumOnRead. Background scrub always checksums and enqueues repairs.
Built-in management API (/api/console, JSON) and web dashboard (React):
| Feature | Endpoint / Details |
|---|---|
| Authentication | Cookie-based login/logout/session check |
| Bucket Management | Create, delete, browse objects |
| Bucket Policy | View and edit bucket policies |
| Pre-signed URLs | Generate upload/download links |
| Large File Upload | Multipart from 5 MiB, 16 MiB parts, 3 concurrent |
| AccessKey Management | Create, delete, list access keys |
| System Settings | Configure max upload size |
| Audit & Metrics | Audit log, hourly request statistics dashboard |
| Cluster Status | Node/disk health, diagnostics export (/api/console/cluster, /api/console/diagnostics) |
| Monitoring Export | Prometheus /metrics, optional OpenTelemetry (OTLP) |
| Background Tasks | Unified management with manual triggers — heartbeat, disk health, metadata consistency, storage GC, repair, rebalance, lifecycle, replication worker |
| Rate Limiting | Fixed-window limits for Console login, Console API, and S3 data plane |
The following features are not present in the current codebase:
- 🔲 Replication — Cross-bucket/bucket replication rules
- 🔲 Object Lock / Retention — WORM compliance
- 🔲 IAM / STS — Full identity and access management model
- 🔲 Distributed Sharding / EC — Multi-node data sharding and erasure coding across nodes
| Dependency | Version | Notes |
|---|---|---|
| .NET SDK | 10.0 | Required for building and running |
| Node.js | 20+ | Only needed for frontend development |
# Restore dependencies
dotnet restore Means.slnx
# Build the solution
dotnet build Means.slnx
# Run with the 'http' launch profile
dotnet run --project src/Means/Means.csproj --launch-profile httpThe development server starts at http://localhost:5178.
Single-node (default XlFs):
docker compose up -d --buildAccess: http://localhost:5178
Default credentials (override via .env or environment variables for production):
- Console:
meansadmin/meansadmin-local - S3:
meansadmin/meansadmin-local-secret
Multi-node (experimental):
docker compose -f compose.multinode.yaml up -d --build| Node | Address |
|---|---|
means-node1 | http://localhost:5181 |
means-node2 | http://localhost:5182 |
means-node3 | http://localhost:5183 |
⚠️ Important: The multi-node setup is for topology and operations page validation only. Each node has an independent XlFs namespace. Do not place a data-plane load balancer in front of these nodes until distributed metadata/RPC is implemented.
Open http://localhost:5178 and sign in with the default development credentials:
| Field | Value |
|---|---|
| Username | admin |
| Password | meansadmin |
Same-origin alias (development):
http://localhost:5178/s3/{bucket}/{key}
Standard host styles (requires DNS/hosts configuration):
| Style | URL Pattern |
|---|---|
| Path-style | http(s)://api.means.local/{bucket}/{key} |
| Virtual-hosted-style | http(s)://{bucket}.means.local/{key} |
All configuration is in src/Means/appsettings.json under the Means section.
| Key | Default | Description |
|---|---|---|
Means:S3:ServiceHost | api.means.local | Path-style hostname |
Means:S3:DomainSuffix | means.local | Virtual-hosted-style domain suffix |
Means:S3:AliasPrefix | /s3 | Same-origin S3 alias prefix |
| Key | Default | Description |
|---|---|---|
Means:Storage:ObjectsPath | data/objects | Single-disk directory (when Disks not configured) |
Means:Storage:Disks | /data/xlfs/disk1... | XlFs multi-disk root directories; each gets .means.sys/format.json |
Means:Storage:ErasureDataShards | 2 | Reed-Solomon data shards; used when online disks satisfy data+parity |
Means:Storage:ErasureParityShards | 2 | Reed-Solomon parity shards; falls back to full-copy quorum when insufficient disks |
Means:Storage:WriteQuorum | 3 | Minimum disks required for write success |
Means:Storage:ReadQuorum | 1 | Minimum disks required for read success |
Means:Storage:MetaSyncMode | Always | Metadata WAL flush strategy |
Means:Storage:VerifyChecksumOnRead | false | Synchronous SHA256 verification on read (adds full I/O) |
Means:Storage:DefaultAccessKey | meansadmin | Default S3 access key |
Means:Storage:DefaultSecretKey | meansadminsecret | Default S3 secret key |
Means:Storage:MultipartUploadCleanupAgeHours | 24 | Age threshold for incomplete multipart upload cleanup |
Means:Storage:MultipartUploadCleanupIntervalMinutes | 60 | Cleanup background task interval |
Means:Storage:GarbageCollectionIntervalSeconds | 3600 | Orphan file GC scan interval |
Means:Storage:GarbageCollectionBatchSize | 1000 | Max candidates per GC run |
Means:Storage:GarbageCollectionTempFileAgeMinutes | 60 | Min age for temp/unreferenced file GC eligibility |
Means:Storage:ReplicationIntervalSeconds | 3600 | Replication worker interval (reports status only when no rules configured) |
| Key | Default | Description |
|---|---|---|
Means:RequestLimits:MaxUploadSizeBytes | 1073741824 (1 GiB) | Max per-request upload size |
Means:RequestLimits:MaxConcurrentUploadRequests | 64 | Global concurrency for PUT/UploadPart; returns 503 SlowDown when exceeded |
| Key | Default | Description |
|---|---|---|
Means:RateLimits:Enabled | true | Enable fixed-window rate limiting |
Means:RateLimits:ConsoleLoginPermitLimit | 10 | Requests per console login window |
Means:RateLimits:ConsoleLoginWindowSeconds | 60 | Console login window (seconds) |
Means:RateLimits:ConsoleApiPermitLimit | 600 | Requests per console API window |
Means:RateLimits:ConsoleApiWindowSeconds | 60 | Console API window (seconds) |
Means:RateLimits:S3PermitLimit | 1200 | Requests per S3 data plane window |
Means:RateLimits:S3WindowSeconds | 60 | S3 data plane window (seconds) |
| Key | Default | Description |
|---|---|---|
Means:Telemetry:Enabled | false | Enable OpenTelemetry tracing |
Means:Telemetry:ServiceName | Means | Tracing resource service name |
Means:Telemetry:OtlpEndpoint | (empty) | OTLP exporter endpoint |
Means:Telemetry:SampleRatio | 1.0 | Trace sampling ratio [0, 1] |
| Key | Default | Description |
|---|---|---|
Means:Cluster:InternalAuthToken | (empty) | Inter-node shard RPC token; empty = /api/internal/cluster returns 404 |
Means:Cluster:MaxShardTransferBytes | 5368709120 (5 GiB) | Per-shard streaming transfer limit |
| Key | Default | Description |
|---|---|---|
Means:Console:AdminUser | admin | Admin username |
Means:Console:AdminPassword | meansadmin | Admin password |
Means:Console:SessionHours | 8 | Session duration (hours) |
- Production security: The service refuses to start if default console credentials are detected. Always replace
AdminUser/AdminPasswordandDefaultAccessKey/DefaultSecretKeyfor production. - Multipart cleanup: Background tasks automatically clean up incomplete upload metadata and part files. Frontend cancellation best-effort calls abort, but disk reclamation does not depend on browser request success.
- Read verification: SHA256 verification is off by default on reads. Enable
VerifyChecksumOnReadfor strong verification. Background scrub always checksums. - Rate limiting responses: Console → JSON
SlowDown; S3 → XMLSlowDown; both includeRetry-Afterheader. - Prometheus: Scrape
/metrics. Recommended alerting rules indocs/operations/deployment-observability.md. - OpenTelemetry: Disabled by default. Set
Means:Telemetry:Enabled=trueto collect ASP.NET Core and background task spans. ConfigureOtlpEndpointfor collector export.
dotnet test Means.slnx| Test Project | Focus |
|---|---|
Means.UnitTests | Address resolution, naming rules, policy evaluation, compression logic |
Means.IntegrationTests | S3 full-chain workflows, pre-signed URLs, Console API end-to-end |
Means.ContractTests | SDK protocol YAML spec vs. fixture completeness |
cd web
npm install
npm run dev- Vite dev server proxies
/apiand/s3tohttp://localhost:5178. - Build for production:
npm run build→ outputs tosrc/Means/wwwroot.
| Package | Location | Description |
|---|---|---|
| C# SDK | SDKs/csharp | Full-featured .NET client SDK (Means.Client.csproj) |
| TypeScript SDK | SDKs/typescript/packages/sdk | Browser-safe S3 client |
| TypeScript Node Extension | SDKs/typescript/packages/sdk-node | SigV4 signing & pre-signing for Node.js |
| Protocol Spec (YAML) | SDKs/spec/means-sdk-v1.yaml | Machine-readable API specification |
| Protocol Spec (Markdown) | SDKs/spec/means-sdk-v1.md | Human-readable protocol documentation |
| SDK Examples | SDKs/examples/ | Usage examples in C# and TypeScript |
Means/
├── CHANGELOG.md
├── IMPLEMENTATION_PLAN.md
├── Means.slnx # .NET solution file
├── compose.yaml # Single-node Docker Compose
├── compose.multinode.yaml # Multi-node experimental Compose
├── README.md # This file (English)
├── README.zh.md # Chinese documentation
│
├── src/
│ ├── Means/ # ASP.NET Core host
│ │ ├── Program.cs # Application entry point
│ │ ├── Composition/ # DI composition & service registration
│ │ ├── Configuration/ # Configuration binding & validation
│ │ ├── Endpoints/ # HTTP API endpoints (Console, etc.)
│ │ ├── Middleware/ # ASP.NET Core middleware pipeline
│ │ ├── Services/ # Application services
│ │ ├── Security/ # Authentication & authorization
│ │ ├── Serialization/ # JSON serialization configuration
│ │ ├── Properties/ # Launch profiles, assembly info
│ │ └── wwwroot/ # Built frontend artifacts
│ │
│ ├── Means.Core/ # Core domain & abstractions
│ │ ├── Abstractions/ # Storage interfaces & contracts
│ │ ├── Constants/ # Well-known constants
│ │ ├── Errors/ # Domain error types
│ │ ├── Models/ # Domain models
│ │ ├── Placement/ # Disk placement strategies
│ │ ├── Policies/ # Storage policies
│ │ └── Requests/ # Request model definitions
│ │
│ ├── Means.Infrastructure.XlFs/ # XlFs storage engine
│ │ ├── Store/ # Core store implementation
│ │ ├── LogDb/ # MeansLogDb metadata engine
│ │ ├── Models/ # XlFs-specific models
│ │ └── XlFsOptions.cs # Storage options
│ │
│ └── Means.Protocol.S3/ # S3 protocol layer
│ ├── Addressing/ # Path-style & vhost-style resolution
│ ├── Compression/ # Content negotiation & compression
│ ├── Serialization/ # S3 XML response serialization
│ ├── Signing/ # SigV4 signature validation
│ └── Validation/ # Request validation
│
├── tests/
│ ├── Means.UnitTests/
│ ├── Means.IntegrationTests/
│ └── Means.ContractTests/
│
├── SDKs/
│ ├── csharp/
│ ├── typescript/
│ ├── spec/
│ └── examples/
│
├── web/ # React frontend (Vite + TypeScript)
│ ├── src/ # React components & pages
│ ├── public/ # Static assets
│ ├── vite.config.ts # Vite configuration
│ └── package.json
│
├── docs/ # Documentation site (Next.js)
│ ├── content/docs/ # Documentation content
│ ├── app/ # Next.js app router pages
│ └── source.config.ts
│
└── scripts/ # Utility scripts
├── benchmarks/ # S3 benchmark scripts
└── compatibility/ # S3 client compatibility matrix
MIT — see LICENSE for details.