Metadata-Version: 2.4
Name: leblob
Version: 0.4.0.dev1
Summary: An S3-compatible object store
Author: Stefane Fermigier
Author-email: Stefane Fermigier <sf@abilian.com>
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: System :: Archiving
Requires-Dist: awscrt>=0.38.1
Requires-Dist: cryptography>=44
Requires-Dist: cyclopts>=5.3.3
Requires-Dist: defusedxml>=0.7.1
Requires-Dist: httptools>=0.6.4
Requires-Dist: uvicorn>=0.54.0
Requires-Dist: uvloop>=0.21.0
Requires-Python: >=3.12
Project-URL: Repository, https://git.sr.ht/~sfermigier/theblob
Project-URL: Changelog, https://git.sr.ht/~sfermigier/theblob/tree/main/item/CHANGES.md
Project-URL: CI, https://builds.sr.ht/~sfermigier/theblob
Description-Content-Type: text/markdown

# leBLOB

An S3-compatible object store written in Python. It starts as one command on one machine, and is designed to grow into a redundant cluster.

It is meant to replace self-hosted MinIO: single-server installs first, then clusters (not there yet).

## Quick start

```sh
uv sync
uv run leblob serve --data-dir ./data
```

The first start creates an access key and prints it once. Use it with any S3 client:

```python
import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="http://127.0.0.1:9000",
    aws_access_key_id="...",
    aws_secret_access_key="...",
    region_name="us-east-1",
)
s3.create_bucket(Bucket="my-bucket")
s3.put_object(Bucket="my-bucket", Key="hello.txt", Body=b"hello")
```

Other commands: `leblob key create|list|delete|grant|revoke` and `leblob fsck`. Run `leblob serve --help` for the server options, including TLS and virtual-hosted addressing.

Presigned URLs need SigV4. boto3 presigns with SigV2 by default, which leBLOB refuses, so create the client with `config=Config(signature_version="s3v4")`.

## Moving from MinIO or Garage

With a [rclone](https://rclone.org) remote for each store (`provider = Other`, `force_path_style = true`), and the bucket created on leBLOB:

```sh
rclone sync --metadata old:my-bucket leblob:my-bucket
rclone check old:my-bucket leblob:my-bucket
```

`tests/bench/run_migration.py` checks this from both: the bytes, `Content-Type`, `Cache-Control` and `x-amz-meta-*` arrive, including multipart objects and unusual keys. These don't, and need setting again: object tags, ACLs, lifecycle rules, CORS, keys, and any version but the current one. rclone also adds `mtime` and `btime` to the metadata. An object's time on leBLOB is when it was copied.

## In a container

```sh
docker build -t leblob .
docker run -d --name leblob -p 9000:9000 -v leblob-data:/data leblob
docker logs leblob   # the first start prints an access key once
```

Set `--workers` after the image name if the default, one per CPU, uses more memory than you want. The data format is upgraded when a newer version starts: back up first, with `leblob backup`. A store written by an earlier release is tested to open in this one, from samples in `tests/upgrade/stores/`.

## Access keys

A key has full access unless it is created as scoped. A scoped key has no rights until it is granted some, per bucket, at one of three levels: `read`, `write` (which includes read) or `admin` (which also allows deleting the bucket).

```sh
uv run leblob key create --scoped --data-dir ./data
uv run leblob key grant ACCESS_KEY my-bucket read --data-dir ./data
uv run leblob key revoke ACCESS_KEY my-bucket --data-dir ./data
```

A grant or a revocation applies from the next request. A scoped key cannot create buckets, sees only its own buckets when listing, and cannot tell a bucket it has no grant on from one that does not exist. Grants disappear with their bucket, so a bucket deleted and created again starts with none.

## Development

```sh
make test      # the whole test pyramid
make lint      # ruff, ty, pyrefly, mypy
```

Tests are in `tests/`: `a_unit` for pure logic, `b_integration` for real SQLite and files, `c_e2e` for real S3 clients (boto3, AWS CLI, rclone, s5cmd) against a real server over HTTP and HTTPS. The rclone, s5cmd and AWS CLI v2 tests are skipped if those tools are not installed.

## License

Apache-2.0: see `LICENSE`.
