v1.2.3
A security-hardening release — no API or tool changes, fully behavior-compatible. Came out of a full codebase security audit.
Secrets at rest
- Token files (
.env+ token store) are written0600(owner-only), with a chmod repair so files from older versions get tightened. - Your Whoop password is removed from
.envafter a successful login — it was only ever needed for the one bootstrap call. - Deploy/rotation secrets are pushed to Fly over stdin (
secrets import), not argv — no longer visible inps//proc.
HTTP auth server
- OAuth JWTs are signed with a key derived from
MCP_AUTH_TOKEN(HMAC), not the token itself. - Access tokens are audience-bound (RFC 8707): a token's
resourceclaim must match this server's/mcpURL. - Security headers on every response (
X-Frame-Options: DENY,nosniff, CSP) — the connector password form can no longer be framed (clickjacking). - Connector-password gate gets a global brute-force ceiling (independent of the spoofable per-IP key) and a 16-char + character-class floor for user-chosen passwords.
whoop-mcp cloudnow verifies the auth gate post-deploy (asserts unauthenticated/mcp→ 401).
Traffic realism
whoop_compare/whoop_lift_historyfan-outs are paced (≤3 concurrent, jittered) instead of one simultaneous burst;whoop_coach_askpolls on a jittered interval, not a fixed 1.000 s metronome.- Read-only hosts derive a stable install id from the account email; a boot warning fires if the bundled iOS app version goes stale.
.gitignore now excludes every .env* variant; .dockerignore excludes .env* + the deploy record. Full detail in CHANGELOG.md and SECURITY.md.