Metadata-Version: 2.4
Name: grass-redis
Version: 0.1.0
Summary: Redis Sentinel client with mandatory mutual TLS for high-availability Redis clusters
Author: Alex
License: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: redis>=5.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"

# grass-redis

Redis Sentinel client with mandatory mutual TLS.

## Features

- Automatic failover via Sentinel
- Connection pooling and caching
- Read replicas for load distribution
- Mandatory TLS with client certificates

## Installation

```bash
pip install -e libs/python/grass-redis
```

## Quick Start

```python
from pathlib import Path
from grass_redis import (
    RedisSentinelClient,
    SentinelConfig,
    load_passwords_from_env,
    load_sentinel_hosts_from_env,
)

# Load from environment (never hardcode)
sentinel_hosts = load_sentinel_hosts_from_env()  # $REDIS_SENTINEL_HOSTS
passwords = load_passwords_from_env()            # /etc/redis/certs/passwords.env

config = SentinelConfig(
    sentinel_hosts=sentinel_hosts,
    databases=passwords,
    certs_dir=Path("/etc/redis/certs"),
)

with RedisSentinelClient(config) as client:
    primary = client.get_primary("db1")
    primary.set("key", "value")
    
    replica = client.get_replica("db1")
    value = replica.get("key")
```

## Configuration

**Environment (optional):**
```bash
export REDIS_SENTINEL_HOSTS="host1:26379,host2:26379,host3:26379"
```

**Deploy certs via Ansible to `/etc/redis/certs/`:**
- `client.crt` - Client certificate (644)
- `client.key` - Private key (600)
- `ca.crt` - CA certificate (644)
- `passwords.env` - Passwords (600)

**`passwords.env` format:**
```bash
REDIS_SCRAPER_DB1_PASSWORD=xxx
REDIS_SCRAPER_DB2_PASSWORD=yyy
```

Names are converted: `REDIS_SCRAPER_DB1_PASSWORD` → database `scraper-db1`

## API

**SentinelConfig:**
- `sentinel_hosts` - List[(host, port)]
- `databases` - Dict[db_name, password]
- `certs_dir` - Path (default: /etc/redis/certs)

**RedisSentinelClient:**
- `get_primary(db)` - Connection for writes
- `get_replica(db)` - Connection for reads
- `discover_primary(db)` - Get current primary address
- `test_connection(db)` - PING test

See `grass-platform/reference_implementation/python/redis/` for examples.
