Skip to content

Features ​

An overview of what TinyS3 implements. For the operation-by-operation matrix see S3 API support.

Protocol ​

  • AWS SigV4 and SigV2 signatures, header-based and presigned (7-day cap)
  • Streaming chunked signing (STREAMING-AWS4-HMAC-SHA256-PAYLOAD)
  • Browser POST-policy uploads with condition validation
  • Virtual-hosted and path-style addressing
  • Multi-user credentials via IAM; anonymous access via bucket policy / public ACL
  • Auth rate limiting (503 SlowDown), clock-skew rejection

Objects & buckets ​

  • Put/get/head/delete/copy, ranged reads, all conditional headers
  • Multipart uploads including UploadPartCopy with source ranges
  • Bucket & object tagging, bulk delete (1000 objects, quiet mode, preconditions)
  • Versioning with delete markers, version-specific reads, restore
  • Object lock: retention (governance/compliance), legal hold
  • Folders, response-* query overrides, Snowball auto-extract (tar)
  • Bucket notifications — async webhook POSTs on object create/remove, configured via PutBucketNotificationConfiguration with event and prefix/suffix filters
  • Bucket quotas — hard per-bucket byte limits; over-quota writes refused with 507 QuotaExceeded
  • Custom response headers (TinyS3 extension, not S3) — configure extra HTTP headers added to successful object GET/HEAD responses per bucket, managed via the admin API (/buckets/{bucket}/response-headers). Useful for serving assets into a cross-origin-isolated page (Cross-Origin-Resource-Policy, Cross-Origin-Embedder-Policy). Reserved/protocol headers and header-injection attempts are rejected.

Access control ​

  • Canned and explicit-grant ACLs (group and email grantees)
  • Bucket policies with condition keys (s3:prefix, tags, encryption headers, copy-source, …)
  • Public access block, ownership controls, policy status
  • Front-door IP access control — a blocklist (deny listed IPs/CIDRs) and a default-deny allowlist (when enforced, only listed IPs pass), enforced on the trusted-proxy-resolved client IP ahead of routing and auth, persisted cluster-wide. Loopback always passes and both have a kill-switch, so a bad rule is always recoverable. Managed from the console (Settings → IP Access Control) and the admin API.

Encryption ​

ModeKey managementNotes
SSE-CCustomer-provided per requestKey never stored server-side
SSE-S3Server master key or KMSRequires TINY_S3_SSE_MASTER_KEY (or a KMS provider)
SSE-KMSServer master key or KMSSame key source; KMS key IDs bind the data key
At-rest shard encryptionTINY_S3_DISK_ENCRYPTION_KEYErasure shards AES-256-CTR + keyed MAC

Requests demanding server-side encryption without a configured master key are refused with 501 — never silently stored in cleartext.

Envelope encryption & external KMS. Optionally, SSE objects are encrypted with a random per-object data key that is wrapped by a pluggable KMS provider and stored (wrapped) in the object's metadata. Providers (kms.provider): local (wraps under the TINY_S3_SSE_MASTER_KEY) and bitwarden (the root key is held in Bitwarden Secrets Manager and fetched at startup). It is opt-in — declare a kms: block to turn it on; without one, SSE uses the legacy master-key scheme and existing deployments are unchanged. The provider is an isolated module; adding another backend never touches the storage core. Objects written before this (no envelope) still decrypt via the legacy key path. See Configuration.

Storage & resilience ​

  • Simple filesystem or Reed-Solomon erasure coding (default 4+2, tolerates 2 disk losses)
  • Background health scanner detects and repairs missing/corrupted shards
  • Tombstone-based fault-tolerant delete; per-write shard generations
  • Lifecycle expiration (current- and noncurrent-version expiration, expired-delete-marker cleanup, abort-incomplete-multipart) and transition (cloud tiering) rules. A Transition moves aged objects' bytes to a configured tier (a saved remote S3 endpoint + bucket) and leaves a local stub; GET streams them back transparently. A transition to a storage class with no configured tier is refused (501) rather than accepted and ignored. Tiers are managed via the admin API (/rest/api/v1/tiers). The transition is crash-safe: the tier pointer is written durably before the local bytes are freed, so a crash mid-transition leaves the object fully local, never lost. Tier endpoints must use https (object data crosses the network), are SSRF-guarded at both save time and connect time, and a tiered read is verified against the stored size (and ETag, for single-part objects) before any bytes are served — a tier returning the wrong, truncated, or substituted object is refused, not streamed under a valid-looking ETag. (v1: encrypted, inline, and versioned-bucket objects are not tiered; deleting a transitioned object best-effort removes the tier copy.)

Bucket replication ​

Rule-driven, asynchronous replication to a local or remote bucket — durable queue with retries and a background scanner, per-object x-amz-replication-status, delete-marker propagation, metadata mutations, existing-object replication, loop-safe two-way, SSE re-encryption, and an admin resync endpoint. See Bucket replication.

IAM & STS ​

The AWS IAM API: users, groups, roles, managed & inline policies, access keys — persisted and enforced on every S3 request. See IAM API.

STS temporary credentials — AssumeRole and GetCallerIdentity. A caller allowed by a role's trust policy receives short-lived session credentials (access key, secret, session token) that authorize S3 requests against the role's policies. Session tokens are stateless (HMAC-signed with a key derived from the root secret), so they survive restarts and work cluster-wide with no session store; the session secret is never transmitted after the AssumeRole response. Temporary sessions cannot manage IAM or mint further credentials, and the trust-policy subset evaluates Effect/Action/Principal — a statement carrying a Condition fails closed (never grants) rather than ignoring it. Session duration is bounded to AWS's 900–43200s.

S3 Select ​

SQL over CSV and JSON objects with proper event-stream response framing.

Static website hosting & logging ​

Per-bucket website hosting with custom domains: map a domain to a bucket — either the ?customDomain mapping (bucket keeps its own name) or by naming the bucket after the domain — point DNS at the server, and requests arriving with that Host are served as a website: index-document resolution (/ and prefix/ → index.html), error documents on 404, anonymous access via the bucket policy. bucket.<server-host> subdomains work too. Website serving is Host-based — it runs ahead of the console/REST routing, so a mapped domain's / and /index.html resolve to the bucket's objects, never the console. (The bare API host is not a website endpoint; use a mapped domain or subdomain.) Plus S3 server access logging to a target bucket.

Operability ​

  • Native TLS
  • Prometheus metrics (/tinys3/metrics, text/plain; version=0.0.4) and JSON metrics (/rest/api/v1/metrics) — request and error counters labelled by operation, HTTP-status class and error code; per-operation latency summaries (_sum/_count) plus a max gauge; upload/download and disk-I/O byte counters; storage-capacity gauges (total/free/used); and, in cluster mode, cluster health (nodes up, write quorum, rack fault tolerance). Point a Prometheus scrape at /tinys3/metrics.
  • Distributed tracing — optional OpenTelemetry (OTLP) export of one SERVER span per S3 request (operation, bucket/key, status, latency). Off by default; enable with the telemetry: block or TINY_S3_OTEL_ENABLED / TINY_S3_OTEL_ENDPOINT. A build without a collector opens no connection.
  • Configurable logging — one named, rotating log file (async, no request-path cost) plus the console, via logging: in the config or TINY_S3_LOG_FILE / TINY_S3_LOG_LEVEL
  • Audit logging — one structured JSON record per S3 request (operation, bucket/key, principal, status, latency, bytes, source, TLS) for security/compliance retention, emitted off the request thread to a JSON-lines file and/or a tamper-evident WORM archive bucket (versioned + object-lock, hash-chained, with per-flush manifest seals). The console Audit page adds a live request tail and a paginated archive browser. Independent of S3 access logging; off by default. Configured via the audit: block or TINY_S3_AUDIT_ENABLED / TINY_S3_AUDIT_FILE. Never records credentials, signatures, or keys; delivery is best-effort (drops with a sealed AUDIT_GAP marker under backpressure rather than slowing a request)
  • Tunable write durability (storage.fsync: none/data/full) — fsync object data before it is visible, optionally the rename too; mode-aware default
  • REST admin API: server info, erasure health, healing, transfer & sync jobs
  • Web console at /console/
  • Public endpoint via Cloudflare Tunnel — optional opt-in plugin that exposes the server on a public https:// URL (--allow-cloudflared-public-endpoint)

Not implemented ​

  • STS AssumeRoleWithWebIdentity (OIDC federation), cross-account principals
  • Analytics family (?metrics, ?inventory, ?analytics, ?intelligent-tiering)
  • BitTorrent (?torrent — deprecated by AWS), Object Lambda, S3 Express

TinyS3 is AGPLv3 licensed. Cloudflare and Cloudflare Workers are trademarks of Cloudflare, Inc. Amazon S3 and AWS are trademarks of Amazon.com, Inc. or its affiliates. MinIO is a trademark of MinIO, Inc. They are named only to describe compatibility; totis.ai is not affiliated with, endorsed by, or sponsored by any of them.