{"openapi":"3.1.0","info":{"title":"Stellarhound","version":"0.1.0"},"paths":{"/healthz":{"get":{"summary":"Healthz","operationId":"healthz_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/version":{"get":{"summary":"Version","operationId":"version_v1_version_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/conditions":{"get":{"summary":"Conditions","description":"未来 7-16 晚逐晚评分卡(v2:云×月,回答\"今晚天气月相有多有利\")。\n\nhourly=true 时每晚附 cloud.hourly(正午→次日正午逐小时加权云量),供 sky view\n时间滑条按小时取云况;权重与逐晚评分同源(skycore RULES),不另立算法。\n\n光污染不进逐晚分数,改由 header.light_pollution.ceiling 单列为\"现实能看到\n什么\"的地点天花板(见 score-v2-redesign.md)。best_window 仍取最高分,语义\n现在是\"天气月相最好的夜\"。","operationId":"conditions_v1_conditions_get","parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number","title":"Lat"}},{"name":"lon","in":"query","required":true,"schema":{"type":"number","title":"Lon"}},{"name":"nights","in":"query","required":false,"schema":{"type":"integer","maximum":16,"minimum":1,"default":16,"title":"Nights"}},{"name":"hourly","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Hourly"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/aurora":{"get":{"summary":"Aurora","description":"极光专线:NOAA SWPC Kp 预报原始时间轴 + 该地点地磁纬度判读。\n\n短时预报、本质不确定——opportunity.note 与 attribution 明说;required_kp 回答\n\"这里要多大的 Kp 才有戏\",None = 即使 Kp9 也不现实(诚实答案,前端原样展示)。","operationId":"aurora_v1_aurora_get","parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number","title":"Lat"}},{"name":"lon","in":"query","required":true,"schema":{"type":"number","title":"Lon"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/agenda":{"get":{"summary":"Agenda","description":"今晚排好序的天象议程 + 本周看点(方向1整合:一页跨所有类别的本地时刻表)。\n\n复用 conditions 的天气/ceiling;7 类天象本地/缓存计算;统一 worth-it 排序由\nskycore.rank_agenda(确定性+版本化)完成。任一源不可用 → sources 显式标注,\n不编造(铁律 1)。彗星星等/极光可见性显式标注 estimate/uncertain。","operationId":"agenda_v1_agenda_get","parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number","title":"Lat"}},{"name":"lon","in":"query","required":true,"schema":{"type":"number","title":"Lon"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/dark-sites":{"get":{"summary":"Dark Sites","description":"附近暗夜点,先按**可驾驶性分层**、再层内按\"未来最佳天气窗口 × 暗度\"排序。\n\n痛点:城市用户拿到的\"最优\"点常是 300 km 外最暗的认证点,近点被埋。修法(M5):\n- 分层优先于纯暗度:先用 skycore.drivability(直线距离 × 1.3 道路迂回系数,估算,\n  非精确路线)把候选分成 reachable_tonight / evening_trip / weekend_trip 三层,近层\n  永远排在远层前面;\n- 层内仍按未来 7 晚最佳天气分(云+月,v2)排序,Bortle 暗度作次级 tiebreak 并展示;\n- 空层诚实(铁律 1):某层无认证点时显式说明(如纽约 reachable_tonight 常为空),\n  给出最近认证点距离并指向光污染地图,**不编造具体点**。\n每层最多返回 limit 个点。","operationId":"dark_sites_v1_dark_sites_get","parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number","title":"Lat"}},{"name":"lon","in":"query","required":true,"schema":{"type":"number","title":"Lon"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":12,"minimum":1,"default":5,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/alerts/status":{"get":{"summary":"Status","description":"前端探测 + GTM 周报读数。subscribers 只是聚合计数(无邮箱/城市明细),计数失败不影响\nenabled 判断(表单可见性不该被一次 DB 抖动拖垮)。","operationId":"status_v1_alerts_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/alerts/subscribe":{"post":{"summary":"Subscribe","operationId":"subscribe_v1_alerts_subscribe_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscribeBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/alerts/confirm":{"get":{"summary":"Confirm","operationId":"confirm_v1_alerts_confirm_get","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/alerts/unsubscribe":{"get":{"summary":"Unsubscribe","operationId":"unsubscribe_v1_alerts_unsubscribe_get","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/consent-region":{"get":{"tags":["privacy"],"summary":"Consent Region","description":"Return whether this request should see the analytics consent choice.\n\n`CF-IPCountry` is added by Cloudflare IP Geolocation on production traffic.\nA missing/unknown value fails closed so an edge-header outage or\ndirect-origin request cannot silently enable analytics. Local and preview\nhosts never call this endpoint because the client-side production-host gate\nruns first. The response must never be shared by a CDN cache between\nvisitors.","operationId":"consent_region_api_consent_region_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/visitor-country":{"get":{"tags":["privacy"],"summary":"Visitor Country","description":"Return the visitor's two-letter country code from Cloudflare, or null.\n\nCountry granularity only — deliberately non-identifying (same posture as\n/consent-region; coordinates never enter this path). The browser uses it to\npre-select which flagship-city quick links to show before any location is\ntyped. Unknown/special Cloudflare values (missing header, XX = unknown,\nT1 = Tor) return null and the client falls back to a global default set.\nThe response must never be shared by a CDN cache between visitors.","operationId":"visitor_country_api_visitor_country_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"SubscribeBody":{"properties":{"email":{"type":"string","title":"Email"},"city":{"type":"string","title":"City"}},"type":"object","required":["email","city"],"title":"SubscribeBody"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}