Skip to content

Commit 154f578

Browse files
committed
Prepare hashavatar-api 1.0.0
1 parent ff95836 commit 154f578

9 files changed

Lines changed: 419 additions & 136 deletions

File tree

Cargo.lock

Lines changed: 16 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "hashavatar-api"
3-
version = "0.13.0"
3+
version = "1.0.0"
44
edition = "2024"
55
rust-version = "1.95"
66
description = "Public avatar API and landing page for hashavatar"
@@ -11,16 +11,16 @@ documentation = "https://github.com/valkyoth/hashavatar-api"
1111

1212
[dependencies]
1313
aws-config = { version = "1.8.16", default-features = false, features = ["rt-tokio"] }
14-
aws-sdk-s3 = { version = "1.131.0", default-features = false, features = ["rt-tokio", "sigv4a"] }
15-
hashavatar = "0.13.0"
14+
aws-sdk-s3 = { version = "1.132.0", default-features = false, features = ["rt-tokio", "sigv4a"] }
15+
hashavatar = "1.0.0"
1616
image = { version = "0.25.10", default-features = false, features = ["png"] }
1717
axum = { version = "0.8.9", default-features = false, features = ["http1", "tokio", "query", "json"] }
1818
getrandom = { version = "0.4.2", default-features = false }
1919
ipnet = { version = "2.12.0", default-features = false }
20-
lru = { version = "0.16.4", default-features = false }
20+
lru = { version = "0.18.0", default-features = false }
2121
serde = { version = "1.0.228", default-features = false, features = ["derive"] }
2222
serde_json = { version = "1.0.149", default-features = false, features = ["std"] }
2323
sha2 = { version = "0.11.0", default-features = false, features = ["alloc"] }
24-
tokio = { version = "1.52.1", default-features = false, features = ["macros", "rt-multi-thread", "net"] }
24+
tokio = { version = "1.52.3", default-features = false, features = ["macros", "rt-multi-thread", "net"] }
2525
tracing = "0.1.44"
2626
tracing-subscriber = "0.3.23"

Cargo.toml.split-template

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "hashavatar-api"
3-
version = "0.13.0"
3+
version = "1.0.0"
44
edition = "2024"
55
rust-version = "1.95"
66
description = "Public avatar API and landing page for hashavatar"
@@ -10,14 +10,14 @@ homepage = "https://github.com/valkyoth/hashavatar-api"
1010
documentation = "https://github.com/valkyoth/hashavatar-api"
1111

1212
[dependencies]
13-
hashavatar = "0.13.0"
13+
hashavatar = "1.0.0"
1414
axum = { version = "0.8.9", default-features = false, features = ["http1", "tokio", "query", "json"] }
1515
getrandom = { version = "0.4.2", default-features = false }
1616
ipnet = { version = "2.12.0", default-features = false }
17-
lru = { version = "0.16.4", default-features = false }
17+
lru = { version = "0.18.0", default-features = false }
1818
serde = { version = "1.0.228", default-features = false, features = ["derive"] }
1919
serde_json = { version = "1.0.149", default-features = false, features = ["std"] }
2020
sha2 = { version = "0.11.0", default-features = false, features = ["alloc"] }
21-
tokio = { version = "1.52.1", default-features = false, features = ["macros", "rt-multi-thread", "net"] }
21+
tokio = { version = "1.52.3", default-features = false, features = ["macros", "rt-multi-thread", "net"] }
2222
tracing = "0.1.44"
2323
tracing-subscriber = "0.3.23"

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ audit, SBOM, reproducibility, smoke, and GitHub CodeQL default setup checks.
1111

1212
## Current Status
1313

14-
The current service version is `0.13.0`.
14+
The current service version is `1.0.0`.
1515

1616
Implemented now:
1717

@@ -25,7 +25,7 @@ Implemented now:
2525
- Namespace-aware tenant and style-version parameters.
2626
- SHA-512 identity hashing.
2727
- WebP avatar responses.
28-
- Avatar families from `hashavatar 0.13.0`: `cat`, `dog`, `robot`, `fox`,
28+
- Avatar families from `hashavatar 1.0.0`: `cat`, `dog`, `robot`, `fox`,
2929
`alien`, `monster`, `ghost`, `slime`, `bird`, `wizard`, `skull`, `paws`,
3030
`planet`, `rocket`, `mushroom`, `cactus`, `frog`, `panda`, `cupcake`,
3131
`pizza`, `icecream`, `octopus`, `knight`, `bear`, `penguin`, `dragon`,
@@ -57,7 +57,7 @@ Intentionally external:
5757
| Area | Status |
5858
| --- | --- |
5959
| Service license | `EUPL-1.2` |
60-
| Renderer crate | `hashavatar 0.13.0` |
60+
| Renderer crate | `hashavatar 1.0.0` |
6161
| MSRV | Rust `1.95.0` |
6262
| Runtime container | Wolfi |
6363
| HTTP framework | `axum` |
@@ -98,7 +98,7 @@ Important query parameters:
9898
| `shape` | `square` | Avatar crop shape: `square`, `circle`, `squircle`, `hexagon`, or `octagon`. |
9999
| `format` | `webp` | Output format. Only `webp` is supported for avatar responses. |
100100
| `size` | `256` | Square image size in pixels. |
101-
| `persist` | `false` | Store through configured S3-compatible backend when enabled. |
101+
| `persist` | `false` | Store through configured S3-compatible backend when enabled; uses the stricter storage rate limit. |
102102

103103
### Path Avatar
104104

@@ -117,8 +117,9 @@ GET /v1/avatar/link?id=robot@hashavatar.app&kind=robot&background=white&accessor
117117
```
118118

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

123124
## Limits
124125

@@ -130,7 +131,7 @@ avatar responses do not expose signed-link metadata in response headers.
130131
| Maximum namespace tenant | `64` ASCII path-safe bytes |
131132
| Maximum namespace style version | `64` ASCII path-safe bytes |
132133
| Maximum renderer identity input | `1024` bytes |
133-
| Rate-limit buckets | `16,384` |
134+
| Rate-limit buckets | `65,536` |
134135
| Avatar render timeout | `3s` |
135136
| Storage operation timeout | `5s` |
136137

SECURITY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Supported Versions
44

5-
Security fixes are expected for the latest published `0.13.x` release.
5+
Security fixes are expected for the latest published `1.0.x` release.
66

77
## Reporting a Vulnerability
88

deploy/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ sets `HASHAVATAR_TRUSTED_PROXIES=10.89.42.0/24` and pins the private network to
1515
that subnet so the app only honors `X-Forwarded-For` style headers from the
1616
Fluxheim network. Do not expose the app container port directly to the internet.
1717

18+
The app container is hardened for the expected runtime shape: read-only root
19+
filesystem, no new privileges, all Linux capabilities dropped, and a small
20+
`/tmp` tmpfs for temporary runtime files.
21+
1822
## Files
1923

2024
- `podman-compose.yml`: starts the app and Fluxheim gateway.

deploy/podman-compose.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ services:
1111
PORT: "8080"
1212
PUBLIC_WEBSITE_HOST: "0.0.0.0"
1313
HASHAVATAR_TRUSTED_PROXIES: "10.89.42.0/24"
14+
read_only: true
15+
security_opt:
16+
- no-new-privileges:true
17+
cap_drop:
18+
- ALL
19+
tmpfs:
20+
- /tmp:size=64m,mode=1777
1421
restart: unless-stopped
1522
networks:
1623
- hashavatar
@@ -21,6 +28,8 @@ services:
2128
restart: unless-stopped
2229
stop_signal: SIGTERM
2330
stop_grace_period: 15s
31+
security_opt:
32+
- no-new-privileges:true
2433
depends_on:
2534
- hashavatar
2635
ports:

docs/SECURITY_CONTROLS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ local test and release gates.
77

88
| Risk | Control |
99
| --- | --- |
10-
| Unbounded or expensive rate-limit state | The rate limiter uses bounded `LruCache` storage for O(1) LRU updates, 65,536 buckets, and regression tests for unique attacker keys. Avatar generation routes, including `/og.png`, pass through origin-side rate limits. |
10+
| Unbounded or expensive rate-limit state | The rate limiter uses bounded `LruCache` storage for O(1) LRU updates, 65,536 buckets, and regression tests for unique attacker keys. CPU-bound avatar and Open Graph rendering run through `spawn_blocking` with timeouts, and all avatar generation routes pass through origin-side rate limits. |
1111
| Forwarded-header spoofing | `X-Forwarded-For`, `X-Real-IP`, and `CF-Connecting-IP` are honored only when the direct peer is a configured trusted proxy; `X-Forwarded-For` chains are resolved from the rightmost untrusted address. |
1212
| Verbose internal errors | Internal errors are logged with `tracing`; clients receive a generic static 500 body. |
13-
| Browser-side content confusion | Responses receive CSP, `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, `Permissions-Policy`, `Cross-Origin-Resource-Policy`, and HTML-only `Cross-Origin-Opener-Policy` and `Strict-Transport-Security` headers. |
13+
| Browser-side content confusion | Responses receive CSP, `Strict-Transport-Security`, `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, `Permissions-Policy`, and `Cross-Origin-Resource-Policy`; HTML responses also receive `Cross-Origin-Opener-Policy`. |
1414
| Operational intelligence disclosure | `/metrics` is loopback-only and returns `404` to non-local peers; `/healthz` remains public for load balancers and uptime checks but only returns liveness status. |
15-
| Object-storage metadata disclosure | Standard avatar responses do not expose presigned URLs or object keys in headers. `/v1/avatar/link` is the explicit JSON endpoint for signed-link metadata. |
15+
| Object-storage metadata disclosure | Standard avatar responses do not expose presigned URLs or object keys in headers. `/v1/avatar/link` is the explicit JSON endpoint for signed-link metadata and returns a hashed cache key rather than the raw identity-bearing cache key. Direct avatar requests with `persist=true` use the same stricter storage rate limit as signed-link requests. |
1616
| S3 prefix escaping | Tenant and style-version namespaces are limited to ASCII letters, digits, hyphens, and underscores before object keys are built. |
1717
| Oversized avatar namespace or identity input | The service caps identities at 512 bytes and namespace components at 64 bytes before rendering. |
1818
| Reflected error content | Client-facing `400` responses use static validation messages for parser and renderer errors instead of forwarding raw library error strings. |

0 commit comments

Comments
 (0)