{"status":"ok","service":"aurae","version":"5.1","timestamp":"2026-08-14T05:31:27.330Z","architecture":{"atlas":"on-device — birth data never transmits","claude":"cloud — all responses sanitised by PRISM","meridian":"local-only — emotional data never transmits","weather":"removed v2.9 — direct from device"},"security":{"appAttest":{"status":"active","teamId":"5KP386UDP6","bundleId":"com.yogeshgahlot.aurae","registeredKeys":18,"timestampWindow":"300s","keyTtl":"30 days","fallback":"x-app-secret (simulator + transition)"},"inputSanitisation":"active — AEGIS only (no Vedic on input)","outputSanitisation":"active — PRISM 80+ rules on output only","artifactCorrection":"active — detectHaikuArtifacts()","rateLimiting":"100/hour + 500/day per device","injectionDetection":"active — 17 patterns","sentinelExtraction":"active — JSON envelope + tag fallback","chunkBoundaryFix":"active — sentence-buffered streaming","streamErrorVisibility":"active v3.0 — real Anthropic errors forwarded","rawErrorBody":"active v3.1 — JSON parse failure logs raw_preview","promptCachingBeta":"removed v3.1 — header was potentially causing 500s","clinicalLanguageSoftening":"active","vedicTermReplacement":"active — output only, Western","siderealTermStripping":"active","fatalisticLanguage":"softened"},"waitlist":{"count":1,"note":"anonymous — no user data stored"},"secrets":{"APP_SECRET":"set","ANTHROPIC_API_KEY":"set","APNS_KEY_ID":"set","APNS_TEAM_ID":"set","APNS_AUTH_KEY":"set (257 chars)"},"push":{"cron":"0,30 * * * * (every :00 and :30 — exact UTC hour+minute match)","note":"V1.1: preferredMinuteUTC stored and matched exactly — half-hour-offset timezones (Newfoundland UTC-3:30) handled without approximation. KNOWN GAP: timezone change after registration makes stored UTC time stale — flagged for V1.5."},"kv":"bound","privacy":{"birthData":"never transmits — ATLAS on-device","emotionalData":"never transmits — MERIDIAN local-only","conversationData":"never stored — processed in-flight only","userInput":"never modified for Vedic terms — passes clean"},"changelog":{"v52":"handleRegisterDeviceToken: delete notif_content_{hash} on every registration. Fixes stale personalised content being delivered to a fresh/reset account — device re-registers → old KV content cleared → cron uses generateCronContent() until new account syncs fresh content via /sync-notification-content.","v51":"scoreCronBody: CRON_IMPLIED_ANSWER_PATTERNS added — final-sentence contains check for \"already know/always known/already in you\" semantic zone. Mirrors CandidateScorer impliedAnswerPatterns added Swift-side. CRON_BANNED_CLOSERS hasSuffix check unchanged. Surfaces apply: generateCronContent() only (sync-notification-content scoring is client-side).","v50":"Notification char budget raised 100 → 130 on all paths: scoreCronBody hard-reject, soft-score sweet-spot, fallback truncateToCharBudget call, and cron prompt instruction. truncateToCharBudget proximity floor lowered 0.60 → 0.40 (KEEP IN SYNC with Swift ChartSanitiser). iOS side mirrored: enforceWordLimit maxCharacters 100 → 130, fit-preference scoring (+2 raw≤130, -1 raw>160), tiebreaker on rawCharCount.","v49":"generateCronContent(): cap() applied to title and body — model-lowercase fragments now display correctly. Prompt updated: \"First word capitalised.\" added to title instruction.","v48":"/clear-notification-cache route — deletes notif_content_{hash} only, returns {deleted, key/reason}. Auth-required, 5/60s rate limit. Enables DevPanel fallback-path test without dashboard access.","v47":"handleScheduled(): 410 BadDeviceToken now deletes device_token_{hash} from KV immediately — stale tokens no longer accumulate failed sends indefinitely.","v46":"generateCronContent(): scoreCronBody now hard-rejects bodies >100 chars (was soft-score only). truncateToCharBudget() added — JS port of Swift ChartSanitiser: sentence → clause (60% guard) → word+ellipsis. Fallback path (all candidates fail) applies truncation instead of returning raw. sendScheduledPush(): apns-expiration header added (now+3600s) — APNs retries for 1h if device is offline at send time.","v41":"Half-hour timezone support — preferredMinuteUTC stored + matched. Cron changed to 0,30 * * * * for exact :00 and :30 delivery. /debug-push-now route for DevPanel test push (auth-required, no idempotency). Beat 1/2 Oracle prompt updated per product spec.","v40":"Hourly cron (0 * * * *) with per-device UTC-hour matching. /sync-notification-content route (notif_content_{hash}, 2-day TTL). preferredHourUTC + timezoneIdentifier stored at /register-device-token. Per-device idempotency key (notif_sent_{hash}_{date}, 26h TTL). Personalised content used when available; generateCronContent() fallback.","v33":"/register-device-token route (KV: device_token_{hash}). Daily cron 14:00 UTC — generateCronContent() + sendScheduledPush() per token. sendScheduledPush() now accepts body param for direct-content push (no NSE needed).","v31":"Removed anthropic-beta prompt-caching header from both handleClaude() and handleStream(). Added raw_preview to JSON parse failure response in handleClaude().","v30":"Stream error visibility — real Anthropic error/status forwarded to device on /stream failures, rate limit headers forwarded","v29":"PRISM hardening — Haiku artifact detection, Vedic output-only, /weather removed, waitlist counter","v28":"App Attest — hardware attestation replaces static secret","v27":"SENTINEL fixed — JSON envelope, MERIDIAN local-only","v26":"ATLAS Edition — /birth-chart and /transits removed"}}