atp.pics#
HTTP service that resolves AT Protocol user identifiers to avatar images, caches them in S3, and redirects clients to the cached image.
Usage#
GET /{identifier}
identifier may be an AT Protocol handle (e.g. alice.bsky.social) or a DID (e.g. did:plc:abc123).
Query parameters#
| Parameter | Type | Default | Description |
|---|---|---|---|
w |
integer | — | Output width in pixels |
h |
integer | — | Output height in pixels |
q |
integer (1–100) | 85 | Encode quality (WebP and JPEG only) |
f |
webp | jpg | png |
webp |
Output format |
When both w and h are provided the image is cover-cropped to exact dimensions.
When only one dimension is provided the image is scaled proportionally.
Response#
A 302 Found redirect to the public S3 URL of the cached image. The redirect itself carries no Cache-Control header so browsers always re-check for avatar changes. The S3 objects are served with Cache-Control: public, max-age=31536000, immutable.
Running#
Docker (recommended)#
docker build -t atp-pics .
docker run -p 8080:8080 \
-e BUCKET_NAME=my-bucket \
-e AWS_REGION=us-east-1 \
-e AWS_ACCESS_KEY_ID=... \
-e AWS_SECRET_ACCESS_KEY=... \
atp-pics
Local development#
Requires a C compiler and libwebp development headers for CGO.
On macOS: brew install webp
On Debian/Ubuntu: apt install libwebp-dev
On Alpine: apk add libwebp-dev
CGO_ENABLED=1 go build ./cmd/server
Environment variables#
| Variable | Required | Default | Description |
|---|---|---|---|
BUCKET_NAME |
yes | — | Storage bucket name |
AWS_REGION |
yes | — | AWS/Tigris region (use auto for Tigris) |
AWS_ACCESS_KEY_ID |
no* | — | AWS access key |
AWS_SECRET_ACCESS_KEY |
no* | — | AWS secret key |
AWS_ENDPOINT_URL_S3 |
no | — | Custom S3 endpoint (e.g. https://fly.storage.tigris.dev for Tigris) |
BUCKET_PUBLIC_HOST |
no | — | Custom hostname for public object URLs (e.g. cdn.example.com); overrides the computed bucket URL |
LISTEN_ADDR |
no | :8080 |
Server listen address |
* AWS credentials may be provided via environment variables, credentials file, IAM role, or any other mechanism supported by the AWS SDK default credential chain.
On Fly.io with Tigris, BUCKET_NAME, AWS_ENDPOINT_URL_S3, AWS_ACCESS_KEY_ID, and AWS_SECRET_ACCESS_KEY are injected automatically by flyctl storage create. Set AWS_REGION=auto manually as a Fly.io secret after provisioning.
Bucket setup#
The bucket must allow public read access so clients can follow redirects directly to object URLs. On Fly.io this is achieved by provisioning with flyctl storage create --public (Tigris). Objects are stored under:
avatars/{did}/original/{cid} — raw blob from PDS
avatars/{did}/{cid}/default.webp — no-param WebP output
avatars/{did}/{cid}/w200-h200-q85.webp
avatars/{did}/{cid}/w200-h200-q85.jpg
avatars/{did}/{cid}/w200-h200.png