Skip to content

valkyoth/hashavatar-api

hashavatar.app

hashavatar.app is the public HTTP API and demo website for deterministic, procedural avatars generated by the hashavatar Rust crate.

The service is designed for stable public avatar URLs: bounded request inputs, namespace-aware identities, cache-friendly responses, optional object-storage links, conservative browser security headers, and release gates for dependency, audit, SBOM, reproducibility, smoke, and GitHub CodeQL default setup checks.

Current Status

The current service version is 1.0.0.

Implemented now:

  • Landing page and browser demo at /.
  • Health endpoint at /healthz.
  • Query API at /v1/avatar.
  • Path API at /avatar/{kind}/{identity}/{format}.
  • Optional signed object-storage link endpoint at /v1/avatar/link.
  • OpenAPI metadata at /docs/openapi.json.
  • Deterministic output for stable CDN-backed avatar URLs.
  • Namespace-aware tenant and style-version parameters.
  • SHA-512 identity hashing.
  • WebP avatar responses.
  • Avatar families from hashavatar 1.0.0: cat, dog, robot, fox, alien, monster, ghost, slime, bird, wizard, skull, paws, planet, rocket, mushroom, cactus, frog, panda, cupcake, pizza, icecream, octopus, knight, bear, penguin, dragon, ninja, astronaut, diamond, coffee-cup, and shield.
  • Background modes: themed, white, black, dark, light, transparent, polka-dot, striped, checkerboard, grid, sunrise, ocean, and starry.
  • Bounded in-memory rate limiter storage.
  • Trusted-proxy validation before forwarded IP headers are honored.
  • Generic internal error responses with detailed server-side tracing.
  • Security headers on all responses.
  • Wolfi-based container runtime.
  • Fluxheim deployment example.
  • Local gates for formatting, clippy, tests, security invariants, dependency policy, RustSec advisories, local smoke testing, SBOM generation, and reproducible release builds.

Intentionally external:

  • TLS termination, WAF rules, global rate limits, and bot controls should live at the reverse proxy, CDN, or infrastructure layer.
  • Long-term object storage is optional and configured through S3-compatible environment variables.
  • The reusable renderer lives in the separate hashavatar crate.

Trust Dashboard

Area Status
Service license EUPL-1.2
Renderer crate hashavatar 1.0.0
MSRV Rust 1.95.0
Runtime container Wolfi
HTTP framework axum
Object storage Optional S3-compatible backend
Rate limiter Bounded LRU map
Forwarded IP policy Trusted proxies only
Internal errors Detailed logs, generic client body
Security headers CSP, permissions policy, referrer policy, nosniff, frame denial, CORP, COOP, HSTS
Release evidence fmt, metadata, docs, clippy, tests, deny, audit, smoke, SBOM, reproducibility
Code scanning GitHub CodeQL default setup

Security-control details live in docs/SECURITY_CONTROLS.md. Rendering provenance lives in PROVENANCE.md. URL stability expectations live in VERSIONING.md.

Public API

Query Avatar

GET /v1/avatar?id=cat@hashavatar.app&algorithm=sha512&kind=cat&background=themed&accessory=none&color=default&expression=default&shape=square&format=webp&size=256

Important query parameters:

Parameter Default Notes
id cat@hashavatar.app Public identity input for deterministic rendering.
tenant public Namespace tenant for isolation.
style_version v2 Namespace style rollout version.
algorithm sha512 Identity hash algorithm. Only sha512 is supported.
kind cat Avatar family.
background themed Background mode: themed, white, black, dark, light, transparent, polka-dot, striped, checkerboard, grid, sunrise, ocean, or starry.
accessory none Optional style layer: none, glasses, hat, headphones, crown, bowtie, eyepatch, scarf, halo, or horns.
color default Accent color: default, neon-mint, pastel-pink, crimson, gold, or deep-sea-blue.
expression default Facial expression: default, happy, grumpy, surprised, sleepy, winking, cool, or crying.
shape square Avatar crop shape: square, circle, squircle, hexagon, or octagon.
format webp Output format. Only webp is supported for avatar responses.
size 256 Square image size in pixels.
persist false Store through configured S3-compatible backend when enabled; uses the stricter storage rate limit.

Path Avatar

GET /avatar/cat/cat@hashavatar.app/webp
GET /avatar/fox/fox@hashavatar.app/webp

Path requests use the default tenant, style version, themed background, default style layers, and 256 pixel size.

Signed Storage Link

GET /v1/avatar/link?id=robot@hashavatar.app&kind=robot&background=white&accessory=glasses&color=gold&expression=happy&shape=circle&format=webp&size=256

This endpoint requires object storage configuration. It renders and stores the avatar when needed, then returns object metadata, a signed URL, and a hashed cache key. Standard avatar responses do not expose signed-link metadata in response headers.

Limits

Limit Value
Minimum avatar size 64 pixels
Maximum avatar size 1024 pixels
Maximum service identity input 512 bytes
Maximum namespace tenant 64 ASCII path-safe bytes
Maximum namespace style version 64 ASCII path-safe bytes
Maximum renderer identity input 1024 bytes
Rate-limit buckets 65,536
Avatar render timeout 3s
Storage operation timeout 5s

The service accepts email-shaped identifiers for compatibility, but stable internal ids or one-way hashes are preferred when you want less personal data in URL logs. It validates its own public size, identity, and namespace ranges before calling the renderer. Namespace components may contain only ASCII letters, digits, hyphens, and underscores so they are safe to use in object-storage keys.

Accessory and expression layers apply to character-style avatar families. Object-style families such as planet, rocket, paws, mushroom, cactus, cupcake, pizza, icecream, diamond, coffee-cup, and shield are normalized to accessory=none and expression=default.

Determinism And Caching

Avatar responses are deterministic for the tuple:

tenant + style_version + sha512 + id + kind + background + accessory + color + expression + shape + webp + size

This makes aggressive edge caching appropriate. Avatar responses include:

  • Cache-Control: public, max-age=86400, s-maxage=31536000, immutable
  • CDN-Cache-Control: public, max-age=31536000, immutable
  • Cloudflare-CDN-Cache-Control: public, max-age=31536000, immutable
  • ETag

The recommended production strategy is:

  • use a stable internal user id or one-way hash as id
  • use tenant for product or environment isolation
  • use style_version for visual rollouts
  • change style_version intentionally when cached visuals should change

Running Locally

Requires Rust 1.95.0 or newer.

cargo run

Default bind:

127.0.0.1:8080

Use a different port:

PORT=3011 PUBLIC_WEBSITE_HOST=127.0.0.1 cargo run

Container and deployment examples set PUBLIC_WEBSITE_HOST=0.0.0.0 explicitly so the service is reachable through the configured reverse proxy.

Smoke test a local server:

scripts/smoke_local.sh

Configuration

General:

  • PORT
  • PUBLIC_WEBSITE_HOST
  • HASHAVATAR_TRUSTED_PROXIES

Object storage:

  • HASHAVATAR_S3_BUCKET
  • HASHAVATAR_S3_REGION
  • HASHAVATAR_S3_ENDPOINT
  • HASHAVATAR_S3_PATH_STYLE
  • HASHAVATAR_S3_PREFIX
  • HASHAVATAR_S3_PRESIGN_TTL_SECONDS

HASHAVATAR_TRUSTED_PROXIES accepts a comma or whitespace separated list of IP addresses and CIDR ranges. Forwarded client IP headers are ignored unless the direct peer address matches this allowlist.

Testing And Release Evidence

Run the fast local gate:

scripts/checks.sh

Run the runtime smoke test:

scripts/smoke_local.sh

Run the fuller release gate:

scripts/stable_release_gate.sh check

The repository includes:

  • release metadata validation
  • Markdown link checks
  • security invariant checks
  • clippy with warnings denied
  • unit tests for rate limiting, trusted proxy handling, renderer validation, and internal error disclosure behavior
  • local HTTP smoke tests for health, WebP rendering, security headers, unsupported algorithm/format rejection, and invalid namespace rejection
  • local Podman smoke tests for the Wolfi image when HASHAVATAR_API_GATE_PODMAN=1 is set
  • cargo deny dependency and license policy
  • RustSec advisory scanning
  • SPDX and CycloneDX SBOM generation
  • reproducible release build checks
  • GitHub CodeQL default setup

Deployment

For self-hosting with Podman and Fluxheim, see:

The compose example builds this service with the Wolfi runtime image and places Fluxheim in front of it as the public TLS reverse proxy.

Release tags publish a Wolfi runtime image to GitHub Container Registry:

ghcr.io/valkyoth/hashavatar-api:<version>
ghcr.io/valkyoth/hashavatar-api:<version>-wolfi

Manual workflow runs from the default branch also publish dev, dev-wolfi, latest, and latest-wolfi tags.

Related Projects

License

Licensed under the European Union Public Licence v. 1.2 (LICENSE, LICENSE-EUPL-1.2.txt).

About

Public avatar API and demo site for deterministic procedural avatars, designed for aggressive CDN caching.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

  •  

Packages

 
 
 

Contributors

Languages