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 or three arguments:

- token: parsed from the original aws request's authorization header
- bucket: parsed from the uri path

the validator may also take a third optional argument
- request: dict of the original request (method, path, query, ...)
    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.2.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.2-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.2-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.2-cp313-cp313-macosx_11_0_arm64.whl (5.1 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

object_storage_proxy-0.4.2-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.2-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.2-cp312-cp312-macosx_11_0_arm64.whl (5.1 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

object_storage_proxy-0.4.2-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.2-cp311-cp311-musllinux_1_2_x86_64.whl (7.7 MB view details)

Uploaded CPython 3.11musllinux: musl 1.2+ x86-64

object_storage_proxy-0.4.2-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.2-cp311-cp311-macosx_11_0_arm64.whl (5.1 MB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

object_storage_proxy-0.4.2-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.2-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.2.tar.gz.

File metadata

  • Download URL: object_storage_proxy-0.4.2.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.2.tar.gz
Algorithm Hash digest
SHA256 e5ea96477a49abe1593c3d2667aec9079a6b79bde844c8d967c424d55edd08df
MD5 5a811231882ad31c09c3d7aee65e1cf8
BLAKE2b-256 d050652fb04df6d643f64ae5445a0dffeae16a03abe99ff0f21a794f4568e803

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 1eef7415b656468aa9a56d7d88b7f873d5f38107721efc6982eb8fcfb32a2de9
MD5 55094ef6675947d9cc56db205dee316e
BLAKE2b-256 fe40f60f2bebe536550be2e968cf8c60d42e1b48696e38064c15a561dafd6018

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 dfbc59a64ddc07f2a175e299f4999bff8d68033eb30282ab93e53930f0401980
MD5 e78ed96fd674c3f41d00843eb420735b
BLAKE2b-256 f961888153199ea89ce4790cce3f24ae8820eae7c4633d101a1ac0e6dfe278a3

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 72b4ef59eec40eaee6a545dc58bda6b1940b1b8856f678c006fe22d2ae741d14
MD5 3cc05f16a9ffbb2ca83cb9cce140cd45
BLAKE2b-256 280379966dc16cb87febfed39c1a9449d6b47e4e92ef73fbcb920a555a384ea7

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5d992c420676f7778bb97c966f181cda3e61bbaa781da5149b3384dc400b5f78
MD5 a0ca47661b646f7d13c451043c1d0769
BLAKE2b-256 c0b438799dbf333037fd1e158c008d0a233295c056f4c23f4f5f686b0508db26

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 208de726c2cc76b5759040592ed910eecc02edd6edd4018270cf00e559d92d2d
MD5 826ee9622c75bcef96e00eb87cc54a8e
BLAKE2b-256 a6b2fb237089275ca335ec0c96ac231b68809b6a2f5bc355cd82b769c4c9ee32

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 7e24f4dd7e8595e21656cbd06747783e0aee2543c7b0015d9a479ff6401322f5
MD5 f35cd83aa16917cad2674a065f5df650
BLAKE2b-256 97ea6b62f64b770cdcba5678da56018d720d4dcc2b61c1de0aafc2d79c6cdfa8

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7d54968b7ae33bd5493b19747d80feea50d84359ffd5436d88726db8fbb23756
MD5 cdcfa21a6ce2253020dbb39b8989f05a
BLAKE2b-256 a9614b188984af23e6fb4aaa88810acb2c70f58884fef0edde7f800f1f94b638

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e660f2bb1f01c37117483de391afc3f85dbe8645fde2d0fcd417ff6a0f7bbd31
MD5 8746313145a11bce700fb96989223f34
BLAKE2b-256 044ffd58a3b4f11b81fc98713a454f87388dfc30ea80792be18df62b37b4bb49

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-cp312-cp312-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 0cba60637567b6381dbf9bad89c006378cc7f3bfcc1176f047537ec6bb2b0e4e
MD5 d079c196d11d31688fdeec48e4e68496
BLAKE2b-256 13425f30e36fc473ea409cb204817b481a5f9539fad3970073a43b351420f3e2

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-cp311-cp311-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 d529e6fb1c4a99ce5efc3b8bece9ef1c5f8a495efdcc68a454fdb104c310daec
MD5 44acf2c89b0258767bd6ebe362a3230b
BLAKE2b-256 8890da59b5d6b0cac2d380c933be54c542b1a24267079a114d53900e9697a928

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e74e57de5f6a4c4e8d67bf972e7136d378438022cc12b8ea8197522f2d492fee
MD5 3656819722575fda838acad020db01e4
BLAKE2b-256 7e8dba4e907b9d4790b1f4b8a37e537e147af0f027e77651253e11c55e262eb6

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 512f5779438f3edc75a3c592db1221efb9361b12439f580372694c7db196f964
MD5 5c30e10a20efec2768a53e56698644a9
BLAKE2b-256 c6ca44b18d6c71a6b1910307831ec47e4d0e91d8ddf68afe10bf636924b29e72

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-cp311-cp311-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 9c085f623439cc9568dba226b2a58c8319b16fabc15db6115a5ed87728f7cba8
MD5 a2f6d281f89deb8f1044703d90ce0ce6
BLAKE2b-256 81386f12be120f16f94e25ab75bb2d150033937bc982ae855107408f11c75b72

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for object_storage_proxy-0.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 acb2b9cbfd7963be0eb2b2d333626555ebdac92ff530f5830942ef938d894593
MD5 27b8d4ae2a6ba05b6a47f8bd975db68e
BLAKE2b-256 dcd49154a2396ce7943056bb29315a15e87b4fb7d4227fa53b11c0e48a15c087

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