···11+# blavatar
22+33+A caching proxy for Bluesky avatars with real-time cache invalidation.
44+55+## Overview
66+77+blavatar is an HTTP service that fetches, caches, and serves user avatars from the Bluesky social network. It maintains cache freshness by subscribing to Bluesky's Jetstream for profile update events, automatically invalidating cached avatars when users change their profiles.
88+99+## Features
1010+1111+- File-based avatar caching with automatic invalidation
1212+- Real-time updates via Bluesky Jetstream subscription
1313+- Supports both DIDs and handles as identifiers
1414+- Generates default placeholder avatars for users without avatars
1515+- Scales all avatars to 128x128 JPEG format
1616+- Graceful shutdown handling
1717+1818+## Building
1919+2020+```bash
2121+make build # Compile binary
2222+make install # Build and install
2323+make clean # Remove build artifacts
2424+```
2525+2626+For Docker:
2727+2828+```bash
2929+make release-dev # Build and push dev image
3030+make release # Build and push release image
3131+```
3232+3333+## Configuration
3434+3535+Configuration is done via CLI flags or environment variables:
3636+3737+| Flag | Environment Variable | Default | Description |
3838+|------|---------------------|---------|-------------|
3939+| `--jetstream-url` | `BLAVATAR_JETSTREAM_URL` | `wss://jetstream2.us-west.bsky.network/subscribe` | Jetstream WebSocket URL |
4040+| `--store-path` | `BLAVATAR_STORE_PATH` | `./avatars` | Directory for cached avatars |
4141+| `--listen` | `BLAVATAR_LISTEN` | `:8080` | HTTP server listen address |
4242+4343+## Usage
4444+4545+Start the server:
4646+4747+```bash
4848+blavatar
4949+```
5050+5151+Or with custom configuration:
5252+5353+```bash
5454+blavatar --store-path /var/cache/avatars --listen :3000
5555+```
5656+5757+Using environment variables:
5858+5959+```bash
6060+export BLAVATAR_STORE_PATH=/var/cache/avatars
6161+export BLAVATAR_LISTEN=:3000
6262+blavatar
6363+```
6464+6565+## API
6666+6767+### GET /{identifier}.jpg
6868+6969+Retrieve an avatar by DID or handle.
7070+7171+**Examples:**
7272+7373+```bash
7474+# By handle
7575+curl http://localhost:8080/alice.bsky.social.jpg -o avatar.jpg
7676+7777+# By DID
7878+curl http://localhost:8080/did:plc:abcdef123456.jpg -o avatar.jpg
7979+```
8080+8181+**Response Headers:**
8282+8383+- `Content-Type: image/jpeg`
8484+- `Cache-Control: public, max-age=7200` (for real avatars)
8585+- `Cache-Control: no-cache` (for default placeholder avatars)
8686+8787+**Response Codes:**
8888+8989+- `200 OK` - Avatar returned (real or default)
9090+- `400 Bad Request` - Invalid identifier format
9191+- `500 Internal Server Error` - Fetch or processing failure
9292+9393+## How It Works
9494+9595+1. **Request**: Client requests an avatar via `/{identifier}.jpg`
9696+2. **Resolution**: Handle is resolved to DID if needed
9797+3. **Cache Check**: Local file cache is checked
9898+4. **Fetch**: On cache miss, avatar is fetched from the user's PDS
9999+5. **Processing**: Image is scaled to 128x128 and encoded as JPEG
100100+6. **Caching**: Result is stored in the file cache
101101+7. **Invalidation**: Jetstream consumer listens for profile updates and removes stale cache entries
102102+103103+## License
104104+105105+See LICENSE file for details.