When you activate your Power Claude license, the server returns a short-lived session token (NOT your license key itself). That token is what the proxy and extension actually use day-to-day. The license-key JWT stays on disk and is consulted only to renew the session.
The heartbeat is what keeps the session token fresh.
The lifecycle
- Activation. Client posts license JWT to
/auth/activate.php. Server verifies the Ed25519 signature, checksjtiusage, returns a session token + expiry (default 24h). - Heartbeat. Every ~6 hours (or sooner if
expires_in < 1h), the client posts the current session token to/auth/refresh.php. Server returns a new token with a fresh 24h expiry. - Suspended sessions. If the client is offline when a heartbeat would have fired, it queues the refresh for when network returns. Up to 7 days offline is tolerated before the session is considered abandoned and re-activation is required.
- Revocation. If your license is revoked server-side (e.g., chargeback), the next heartbeat returns 401, the client wipes its session token, and the user is prompted to re-activate.
Why a session token, not the license key
The license JWT is long-lived and powerful. Sending it on every request would mean: every rotation of credentials touches the long-lived secret, every captured request leaks the long-lived secret, every revocation requires re-authing every client. The session token is intentionally short-lived so:
- Compromise of an in-flight session token has a 24-hour blast radius
- Revocation propagates within 6 hours (or instantly on next request)
- The license JWT lives in disk-only storage and is read only during activation/refresh
Auto-resume across machine restarts
The session token persists in ~/.power-claude/state/session.json (mode 0600). When you reboot, the watchdog brings the proxy back up; the proxy reads the session token; if it's still within expiry, no user interaction is needed. The dashboard, extension, and CLI all observe proxy_running: true and session_active: true within seconds.
If the saved session token has expired (you were offline for >24h), the proxy will request a refresh on next use; if the heartbeat succeeds, everything works without a prompt. Only if both the session token AND the refresh fail does the user see "license expired — re-activate."
Inspecting heartbeat state
pc status --json | jq '{session_active, session_expires_at, last_heartbeat_at, next_heartbeat_at}'
Healthy output: session_active: true, next_heartbeat_at is in the near future (≤6h), last_heartbeat_at is within the last 12h.
What does NOT trigger a heartbeat
- Every API call. Heartbeats are time-based, not request-based — they don't add per-request latency.
- Switching active account during rotation. Account rotation is local; the license is independent.
- Toggling Power Mode or any client setting.