An open API service indexing awesome lists of open source software.

https://github.com/thunderbird/avatars

Sender-domain avatar proxy for Thunderbird webmail
https://github.com/thunderbird/avatars

Last synced: 24 days ago
JSON representation

Sender-domain avatar proxy for Thunderbird webmail

Awesome Lists containing this project

README

          

# avatars

Cloudflare Worker that serves sender-domain avatar images for Thunderbird
webmail.

The service accepts a normalized domain on `avatars.thunderbird.net` and
always returns a PNG, falling back to a 1x1 transparent pixel on errors:

```text
https://avatars.thunderbird.net/example.com?v=3
```

The optional `v` query parameter is a cache version chosen by the caller.
Changing it forces a fresh edge/browser cache entry when avatar lookup
behavior changes. It is not sent to upstream providers.

## Behavior

- Accepts only `GET`, `HEAD`, and `OPTIONS`. Other methods get a 405 fallback.
- Normalizes and validates the domain before making any upstream request.
- Fetches favicons only from `https://www.google.com/s2/favicons`. Because
Google reliably redirects this endpoint to its CDN, redirects are followed
and the final response is required to come from `www.google.com` or a
`*.gstatic.com` subdomain over HTTPS. Bare `gstatic.com` and any `http:`
downgrade are rejected.
- Returns only PNG bodies up to 256 KiB, validating the PNG signature, chunk
structure, IHDR/PLTE/IDAT/IEND ordering, color-type/bit-depth combinations,
and CRC-32 of every chunk before responding or caching. Provider response
headers are not trusted.
- Successful responses, validation failures, and Google 4xx responses are all
cached in Cloudflare's default Cache API under a versioned key
(`/__cache//`). Validation failures are negative-
cached for one hour; transient network failures (502) are not cached.
- `HEAD` requests share the `GET` cache and populate it on miss, so a `HEAD`
costs no more than a `GET` and never bypasses the edge.
- Sends wildcard CORS headers, `X-Content-Type-Options: nosniff`, and
`Cross-Origin-Resource-Policy: cross-origin` on every PNG response.
- Keeps Worker observability logging disabled.

### Known limitations

- Google's favicon service responds with a generic "globe" placeholder for
domains it does not know about. The worker has no signal to distinguish
this from a real favicon and will cache the placeholder under the requested
domain for the configured TTL. Operators that want to fail-closed for
unknown domains should change the `v` query parameter or bump
`AVATAR_CACHE_PROVIDER_VERSION` after fixing the heuristic.

## Development

Use Node.js 24 or newer.

```bash
npm ci
npm run typecheck
npm test
```

Run locally with Wrangler:

```bash
npx wrangler dev
```

## Deployment

The production custom domain `avatars.thunderbird.net` is attached to the
Worker once in Cloudflare using human credentials. The steady-state GitHub
Actions deploy updates only the Worker script, so the CI token does not
need DNS or Worker route permissions.

Required repository secrets:

- `CLOUDFLARE_ACCOUNT_ID`
- `CLOUDFLARE_API_TOKEN`

The deploy token is scoped with only Workers script write/edit permissions.