Skip to main content

<object-storage-proxy ⚡> Yet Another Object Storage Proxy

Project description

CI PyPI - Downloads PyPI - version

<object-storage-proxy ⚡> Yet Another Object Storage Reverse Proxy

📌 Note: This project is still under heavy development, and its APIs are subject to change.

Introduction

A fast and safe in-process reverse proxy server, based on Cloudflare's pingora, to reverse proxy AWS and IBM Cloud Object Storage buckets and integrate your Authentication and Authorization services.

  • Compatible with AWS SDK -> aws cli/boto3, polars, spark, datafusion, ...
  • Decouples frontend from backend authentication and authorization.
  • Python interface: pass in callables for credentials fetching, validation, lookup secret for access_key (with cache).
  • Compatibility Gateway between systems that support only 1 hmac credentials pair, and multi-credentials buckets backend.

Implementation

The Server Config accepts the following:

proxy_server_config = ProxyServerConfig(
    cos_map=cos_map,
    bucket_creds_fetcher=do_hmac_creds,
    validator=do_validation,
    http_port=6190,
    https_port=8443,
    threads=1,
    verify=False,
    hmac_keystore=hmac_keys,
    skip_signature_validation=False,
    hmac_fetcher=lookup_secret_key
)
argument description optional default value
cos_map bucket configuration, see below NA
bucket_creds_fetcher python callable to retrieve credentials for a given bucket, to return either api key or hmac key pair NA
validator python callable, validates access for a given token/bucket combination NA
http_port server listener port on http ✅ at least http_port or https_port, or both NA
https_port server listener port on https ✅ at least http_port or https_port, or both NA
threads number of service threads 1
verify ignore ssl verification errors on backend storage (IBM/AWS) (for dev purposes) False
hmac_keystore
skip_signature_validation ignore ssl verification errors on frontend (for dev purposes) False

The bucket dict contains for each bucket:

- endpoint host
- port
- api key (optional)
- hmac access key (optional)
- hmac secret key (optional)
- ttl (optional, default 300) -> keep this reasonably short, but size to your needs
cos_map = {
    "bucket1": {
        "host": "s3.eu-de.cloud-object-storage.appdomain.cloud",
        "port": 443,
        "ttl": 0
    },
    "bucket2": {
        "host": "s3.eu-de.cloud-object-storage.appdomain.cloud",
        "port": 443,
        "apikey": "apikey"
    },
    "proxy-bucket01": {
        "host": "s3.eu-de.cloud-object-storage.appdomain.cloud",
        "port": 443,
        "access_key": "<redacted>",
        "secret_key": "<redacted>",
        "ttl": 300
    },
    "proxy-aws-bucket01": {
        "host": "s3.eu-west-3.amazonaws.com",
        "region": "eu-west-3",
        "access_key": os.getenv("AWS_ACCESS_KEY"),
        "secret_key": os.getenv("AWS_SECRET_KEY"),
        "port": 443,
        "ttl": 300
    }    
}

The Python callables take two arguments: TODO: add prefix for fine-grained validation

- token: parsed from the original aws request's authorization header
- bucket: parsed from the uri path
    def your_credentials_fetcher(token: str, bucket: str) -> str
    def your_request_authorizer(token: str, bucket: str) -> bool

The Problem

secrets

IBM COS Storage is built in a way where buckets are grouped by a cos (Cloud Object Storage) instance. Access to a bucket is managed by either an api key or hmac secrets, configured on the cos instance.

endpoint

Each bucket has its own endpoint: <bucket_name>.s3..cloud-object-storage.appdomain.cloud:.

The port is not always different, though, but it might be. Depends on your implementation.

You can imagine managing multiple buckets across instances can become quite cumbersome, even with aws profiles etc.

solution

There are two ways to access a bucket: through virtual addressing style (bucket.ibm-cos-host:port) and path style (ibm-cos-host/bucket).

your client (aws s3 compatible) -> http(s)://this-proxy/bucket01 -> https://bucket01.s3.eu-de.cloud-object-storage.appdomain.cloud:443

  1. translate path style to virtual style
  2. abstract authentication & authorization

Pass in a function which maps bucket to instance (credentials), and a function to map bucket to port (endpoint)

request lifecycle

request stages

authentication & authorization

The advantage is we can plug in a python authentication function and another function for authorization, allowing for fine-grained control.

authentication

We use the standard aws hmac header and aws v4 request signing algorithm.

authorization

Pass in a callable from python which will be called from rust. This will be cached (ttl) for consecutive requests.

Examples

With local configuration.

~/.aws/config

[profile osp]
region = eu-west-3
output = json
services = osp-services
s3 =
    addressing_style = path

[services osp-services]
s3 =
  endpoint_url = http://localhost:6190

~/.aws/credentials

[osp]
aws_access_key_id = MYLOCAL123  # <-- this could be an internal client identifier, to fetch openid connect/oauth2 token or anything that makes sense for your business
aws_secret_access_key = nothingmeaningful # <-- private key to sign original request

Set up a minimal server implementation:

import json
import os
import random
import object_storage_proxy as osp

from dotenv import load_dotenv

from object_storage_proxy import start_server, ProxyServerConfig


_TRUES  = {"y", "yes", "t", "true", "on", "1"}
_FALSES = {"n", "no", "f", "false", "off", "0"}


def strtobool(val: str) -> bool:
    """Convert a string to True/False, raise ValueError otherwise."""
    v = val.lower()
    if v in _TRUES:
        return True
    if v in _FALSES:
        return False
    raise ValueError(f"invalid truth value {val!r}")


def do_api_creds(token: str, bucket: str) -> str:
    """Fetch credentials (ro, rw, access_denied) for the given bucket, depending on the token. """
    apikey = os.getenv("COS_API_KEY")
    if not apikey:
        raise ValueError("COS_API_KEY environment variable not set")
    
    print(f"Fetching credentials for {bucket}...")
    return apikey


def do_hmac_creds(token: str, bucket: str) -> str:
    """ Fetch HMAC credentials (ro, rw, access_denied) for the given bucket, depending on the token """
    access_key = os.getenv("ACCESS_KEY")
    secret_key = os.getenv("SECRET_KEY")
    if not access_key or not secret_key:
        raise ValueError("ACCESS_KEY or SECRET_KEY environment variable not set")
        
    print(f"Fetching HMAC credentials for {bucket}...")

    return json.dumps({
        "access_key": access_key,
        "secret_key": secret_key
    })

def lookup_secret_key(access_key: str) -> str | None:
    # get all environment variables ending in ACCESS_KEY
    access_keys = [{key:value} for key, value in os.environ.items() if key.endswith("ACCESS_KEY") and value==access_key ]

    if len(access_keys) > 0:
        access_key_var = next((k for k, v in access_keys[0].items() if v == access_key), None)

        secret_key_var = access_key_var.replace("ACCESS_KEY", "SECRET_KEY")
        return os.getenv(secret_key_var, None)
    else:
        print(f"no access keys found for : {access_key}")


def do_validation(token: str, bucket: str) -> bool:
    """ Authorize the request based on token for the given bucket. 
        You can plug in your own authorization service here.
        The token is a client identifier used to fetch an authorization token and further authenticate/authorize.
        The bucket is the bucket name.
        The function should return True if the request is authorized, False otherwise.
    """

    print(f"PYTHON: Validating headers: {token} for {bucket}...")
    # return random.choice([True, False])
    return True


def main() -> None:
    load_dotenv()

    counting = strtobool(os.getenv("OSP_ENABLE_REQUEST_COUNTING", "false"))

    if counting:
        osp.enable_request_counting()
        print("Request counting enabled")

    apikey = os.getenv("COS_API_KEY")
    if not apikey:
        raise ValueError("COS_API_KEY environment variable not set")

    cos_map = {
        "bucket1": {
            "host": "s3.eu-de.cloud-object-storage.appdomain.cloud",
            "region": "eu-de",
            "port": 443,
            "apikey": apikey,
            "ttl": 0
        },
        "bucket2": {
            "host": "s3.eu-de.cloud-object-storage.appdomain.cloud",
            "region": "eu-de",
            "port": 443,
            "apikey": apikey
        },
        "proxy-bucket01": {
            "host": "s3.eu-de.cloud-object-storage.appdomain.cloud",
            "region": "eu-de",
            # "access_key": os.getenv("ACCESS_KEY"),
            # "secret_key": os.getenv("SECRET_KEY"),
            "port": 443,
            "ttl": 300
        },
        "proxy-bucket05": {
            "host": "s3.eu-de.cloud-object-storage.appdomain.cloud",
            "region": "eu-de",
            "access_key": os.getenv("PROXY_BUCKET05_ACCESS_KEY"),
            "secret_key": os.getenv("PROXY_BUCKET05_SECRET_KEY"),
            "port": 443,
            "ttl": 300
        },
        "proxy-aws-bucket01": {
            "host": "s3.eu-west-3.amazonaws.com",
            "region": "eu-west-3",
            "access_key": os.getenv("AWS_ACCESS_KEY"),
            "secret_key": os.getenv("AWS_SECRET_KEY"),
            "port": 443,
            "ttl": 300
        }
    }

    hmac_keys= [
        # {
        #     "access_key": os.getenv("LOCAL_ACCESS_KEY"),
        #     "secret_key": os.getenv("LOCAL_SECRET_KEY")
        # },
        {
            "access_key": os.getenv("LOCAL2_ACCESS_KEY"),
            "secret_key": os.getenv("LOCAL2_SECRET_KEY")
        },

    ]

    ra = ProxyServerConfig(
        cos_map=cos_map,
        bucket_creds_fetcher=do_hmac_creds,
        validator=do_validation,
        http_port=6190,
        # https_port=8443,
        threads=1,
        # verify=False,
        hmac_keystore=hmac_keys,
        skip_signature_validation=False,
        hmac_fetcher=lookup_secret_key
    )

    start_server(ra)


if __name__ == "__main__":
    main()

Run with aws-cli (but could be anything compatible with the aws s3 api like polars, spark, presto, ...):

$ aws s3 ls s3://proxy-bucket01/ --recursive --summarize --human-readable --profile osp
2025-04-17 17:45:30   33 Bytes README.md
2025-04-17 17:48:04   33 Bytes README2.md

Total Objects: 2
   Total Size: 66 Bytes
$

Server output:

$ uv run python test_server.py
2025-04-19T13:19:54.402023+02:00  INFO object_storage_proxy: Logger initialized; starting server on http port 6190 and https port 8443
2025-04-19T13:19:54.402361+02:00  INFO object_storage_proxy: Bucket creds fetcher provided: Py(0x100210680)
Fetching credentials for bucket01...
2025-04-19T13:19:54.402485+02:00  INFO object_storage_proxy: Callback returned: Kn2t...
[src/lib.rs:327:5] &run_args.cos_map = Py(
    0x000000010061aa00,
)
2025-04-19T13:19:54.403738+02:00  INFO pingora_core::server: Bootstrap starting
2025-04-19T13:19:54.403852+02:00  INFO pingora_core::server: Bootstrap done
2025-04-19T13:19:54.424489+02:00  INFO pingora_core::server: Server starting
PYTHON: Validating headers: MYLOCAL123 for proxy-bucket01...
2025-04-19T13:19:58.124729+02:00  INFO object_storage_proxy::utils::validator: Callback returned: false
PYTHON: Validating headers: MYLOCAL123 for proxy-bucket01...
2025-04-19T13:20:00.919320+02:00  INFO object_storage_proxy::utils::validator: Callback returned: true
2025-04-19T13:20:01.181775+02:00  INFO object_storage_proxy::credentials::secrets_proxy: No cached token found for proxy-bucket01, fetching ...
2025-04-19T13:20:01.181859+02:00  INFO object_storage_proxy::credentials::secrets_proxy: Fetching bearer token for the API key
2025-04-19T13:20:01.739385+02:00  INFO object_storage_proxy::credentials::secrets_proxy: Received access token
2025-04-19T13:20:01.739600+02:00  INFO object_storage_proxy::credentials::secrets_proxy: Fetched new token for proxy-bucket01
2025-04-19T13:20:01.739668+02:00  INFO object_storage_proxy: Sending request to upstream: https://proxy-bucket01.s3.eu-de.cloud-object-storage.appdomain.cloud/?list-type=2&prefix=&encoding-type=url
2025-04-19T13:20:01.739922+02:00  INFO object_storage_proxy: Request sent to upstream.

test

See the included python test script.

Create self-signed certificates and export the environment variables:

openssl req -x509 -nodes -days 365 \
  -newkey rsa:4096 \
  -keyout key.pem \
  -out cert.pem \
  -config localhost.cnf
export TLS_CERT_PATH=/full/path/cert.pem
export TLS_KEY_PATH=/full/path/key.pem

Status

  • pingora proxy implementation
  • pass in credentials handler (which may return either api key string or json string with access_key and secret_key )
  • cache credentials
  • pass in bucket/instance and bucket/port config
  • split in workspace crate with core, cli and python crates (too many specifics for python)
  • config mgmt
  • cache authorization (with optional ttl)
  • http frontend (optional)
  • https frontend (supports HTTP/2) (optional)
  • configurable request counting
  • call the api key fetcher callback only once, save to cos map
  • config for #threads in ProxyServerConfig
  • also pass path and method to python callbacks and cache by token/bucket/path/method (identity based access/cache)
  • option to disable upstream/peer certificate validation (for development, not production!)
  • expose pingora proxy server and services configuration to python
  • exclude proxy headers (x-forwarded-proto, x-forwarded-host, ..) for signing
  • https://github.com/aws/aws-cli/issues/9214 (workaround: export AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED)
  • spark streaming writes

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

object_storage_proxy-0.4.1.tar.gz (1.3 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

object_storage_proxy-0.4.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.9 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ x86-64

object_storage_proxy-0.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.9 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

object_storage_proxy-0.4.1-cp313-cp313-macosx_11_0_arm64.whl (5.1 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

object_storage_proxy-0.4.1-cp313-cp313-macosx_10_12_x86_64.whl (5.4 MB view details)

Uploaded CPython 3.13macOS 10.12+ x86-64

object_storage_proxy-0.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.9 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

object_storage_proxy-0.4.1-cp312-cp312-macosx_11_0_arm64.whl (5.1 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

object_storage_proxy-0.4.1-cp312-cp312-macosx_10_12_x86_64.whl (5.4 MB view details)

Uploaded CPython 3.12macOS 10.12+ x86-64

object_storage_proxy-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl (7.6 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ x86-64

object_storage_proxy-0.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.9 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

object_storage_proxy-0.4.1-cp311-cp311-macosx_11_0_arm64.whl (5.1 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

object_storage_proxy-0.4.1-cp311-cp311-macosx_10_12_x86_64.whl (5.4 MB view details)

Uploaded CPython 3.11macOS 10.12+ x86-64

object_storage_proxy-0.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.9 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

File details

Details for the file object_storage_proxy-0.4.1.tar.gz.

File metadata

  • Download URL: object_storage_proxy-0.4.1.tar.gz
  • Upload date:
  • Size: 1.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.8.6

File hashes

Hashes for object_storage_proxy-0.4.1.tar.gz
Algorithm Hash digest
SHA256 407b671f322fe28a2dd889dd07748fb99ea988fd3c3e9e7b194a7abd843c2bfd
MD5 781f14203df8bf6f5508c276592a196d
BLAKE2b-256 c794f718d045ca14278bca75fbc5513cb7bb26f1caf21cb496feb4d4ab545771

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 037e1d773443eb0f5b933c4339811e08f9909a04a8690555b07856ee7578a4e1
MD5 62a332104df20dd674c4ad7f22b17bf1
BLAKE2b-256 6dc857ee9367dbbff9f4644289d974d1e03eb8be8b7d313d42ef29f34b3d1572

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 cc1864720ba34f7880e65f34a523a226049dd25fb7220e576c5edeff484ee819
MD5 ec9d6ff9c243650ecb0e51a93ec26877
BLAKE2b-256 69c0c32d913406a5e3b864d386860667760b47a09d139646925d462290faa3d4

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 1b301859af1a9ce793a1fcbca336f1c2b34d8639757d8c910df21c62a3bcf4fe
MD5 eb7566a347cd7162a281519c40122e34
BLAKE2b-256 4410443506c7174509fc834c8aecddff176f1157c88d12fb5936337ed91854ee

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5c46c1390bcba110d02f5f4c821f920f45e0f329eb95c80c7efc327d1ae71b0f
MD5 416aea8943e5757fb507f06d20bf7921
BLAKE2b-256 f2c599ecc84c3c3f7cc460eb07a1ecec1a20fee352d2c4eedadc5088a2c65d63

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d8a334c0b4853244d001f53cd96fd9e670d3c9379168e7eb87e95b06ef68a2f6
MD5 210e08d04d30e7f5a41b92e0082bd646
BLAKE2b-256 1b56fee72ce733ff0bd2812121201f56640217b4c76a6cc10a14c2d3b8c40809

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-cp313-cp313-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 d81a64f0d411debb92bf7cf526d1a2d38e20182a4af6365c699a0b457d19811a
MD5 1067e4f866bf96aa242500391a81d84d
BLAKE2b-256 002b9a21d086aa29677ed79f57f8ed14ecbb81901f3d8e78f2d9c1ab21c5b24c

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 76580745fce9aa6a62f9e7e05144a604257602bb48a392e16bd1a35baa5cf54c
MD5 8d846f663dfbc0f5a5c1d9b41cabe816
BLAKE2b-256 1dae5d04e3916cc179296a4fbbfbc1a17ff9c151ad5f669c8f427a7a1fedd794

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 53f1cd0c16afb0d3c4a5a0d3d200ade451d58072767ee0d87f58353687b71e44
MD5 a31621bcd267ab47566b20c93f86cf1d
BLAKE2b-256 793500651b4a8271f4d44b806677385f1e92e07eb7ace66957ef19a7550851bc

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-cp312-cp312-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-cp312-cp312-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 426d360b9a92db9f022948309de96222fef2aba84f5a39b6945fe07e8c16ece0
MD5 4cdf1da478a594ef697e54b84ea889ff
BLAKE2b-256 558a0c37905af135368c946873f5ac84455b07936a5bfe09ad4a71b692f4fd05

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 f32b7503159014c0bad5e98679bacf4f8b7aa23d4c6331cbf758283ade5e7d86
MD5 67bea24ce42353462671758dd0661072
BLAKE2b-256 1e0353921353893dc611b12b74905c82b31b2fc0c08065bbadf4ecce1698771f

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 31d03d0a5975d2e8be098abad1b44c4ee3654f8853625aa93fb3aa82e4db6637
MD5 e71be3d9d7f8bcfee623a5ff4d7bfc4a
BLAKE2b-256 7e3d5f14b3b6b4ca97cd7d1d75cd9f28784fda5ef95d448080fb031f31a4fc88

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 78aad38516566a99bc99dc8267590c33efb645d530040e09b9688d3fed53790d
MD5 cf131c7bc172fe50b41bebe5f9f24908
BLAKE2b-256 29e5dd3fbfb3615d35e87143c4f3b603b90a095b75fe2d9330bdc500b2b4730f

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-cp311-cp311-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-cp311-cp311-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 da3ffdac085e667e5b16a8a007e57e45507772acb7b980f82d1721555ef7095a
MD5 65add038caf1c422983f921a2963a1d6
BLAKE2b-256 110249587b11bcbadc71bf40ce68b146480a93ad44f4b1dfc1055e155abb7f7c

See more details on using hashes here.

File details

Details for the file object_storage_proxy-0.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for object_storage_proxy-0.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e0accedb68b82d21d20f6413d7065dd7893edd96eac2dea3c6bc1a2ce26e5f7f
MD5 57a649c857e89ff28007b4e0b2da3db1
BLAKE2b-256 f1513e7027d739cc430b812fb751c2e97086a64965056a0dacce767799d050df

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page