Overview
Orbital Outlook is a Deno-based background service that watches upcoming launches, enriches each mission with trusted news or web context, and posts a single daily briefing to our #outer-space Slack channel. The service runs on Vercel's Deno runtime, stores launch snapshots plus Slack metadata in Neon Postgres, and leans on OpenAI for web search and conversational summarization.
Under the hood we follow a layered approach: domain models describe launches, summaries, and Slack blocks; infrastructure adapters talk to Launch Library 2, Spaceflight News, OpenAI, Slack, and Postgres; and orchestration pipelines compose everything into the hourly cache refresh and the daily publishing run.
Every digest opens with NASA's Astronomy Picture of the Day, dropping the image URL and a trimmed GPT caption ahead of the launch stack so Space Nerds get fresh art alongside the schedule.
The Daily Slack Rhythm
Every morning the bot announces whether launches are scheduled for the requested date and, when nothing is on the pad, automatically rolls the copy forward to the next window. The entry stays pinned so folks can quickly see schedules, vehicle details, and any scrub notices. Same-day launches remain in view after liftoff so the hourly refresh can append the final status once web-search snippets report a confirmed outcome.

Data Pipeline
The hourly pipeline wires together Launch Library 2, Spaceflight News, OpenAI web search, and our persistence layer. Everything is wrapped in a resilient HTTP client that understands retries, conditional requests, and caching headers.
Launch intake
`LaunchService` pulls detailed manifests from Launch Library 2, preserving provider metadata, launch windows, and pad data. ETags are stored in Postgres so we skip unchanged feeds.
News + web context
Spaceflight News lookups and OpenAI web-search queries run in parallel pools, trimming each launch to three high-confidence tidbits for the prompt.
Snapshot cache
Every processed launch plus the rendered summary is written to `daily_launches` and `launch_summaries`, providing a historical record and a fast reuse path for the daily run.
Idempotent orchestration
Summaries are hashed (`computeSummaryHash`) before persisting. If nothing changed, we simply reuse the cached payload and skip writes, which keeps cron executions cheap.
Enrichment & Summaries
`SummaryService.prepareDailySummary` filters launches for the requested timezone, auto-labels the intro (today, tomorrow, or a friendly date), and falls forward when a day is empty. Each launch section carries mission background, provider notes, and formatted schedules so the LLM prompt only has to polish tone—not invent facts. The intro also embeds the APOD image link plus a succinct caption so every Slack post pairs the mission rundown with NASA's featured photo of the day.
- • Astronomy Picture of the Day metadata is cached per run so the HD image + caption stay consistent across the hourly refresh and daily publish.
- • Spaceflight news + search results feed a Launch Status Monitor that spots scrubs, failures, or confirmed liftoffs and surfaces those inside the metadata block, including post-launch updates when fresh coverage lands within the first hour after liftoff.
- • Prompt payloads include the full enriched launch array plus audience/tone hints, and the same payload is stored with the cache for replay or debugging.
Slack Delivery & Pin Management
The notification gateway wraps the Slack Web API for posting, updating, fetching channel history, and reconciling pins. We only post when the digest changes, otherwise the pipeline exits early to avoid rate limits.
Create or update
If a message already exists for the day we update it in place; otherwise we create and immediately persist the timestamp + permalink so future runs know where to operate.
Pin reconciliation
`reconcilePins` guarantees exactly one pinned summary per channel by unpinning anything stale before pinning the latest.
Slack-aware metadata
Launch summaries track the Slack channel, timestamps, and hash inside Postgres, which lets `/daily` skip re-posting even if the cron fires multiple times.
Architecture & Ops Posture
The service exposes `/api/healthz`, `/api/hourly`, and `/api/daily` through an Oak server with per-request correlation IDs. Config is type-safe and validated on boot so missing env vars fail fast.
Dependency container
We register clients (Launch Library, Spaceflight News, Slack, OpenAI, Postgres) and repositories once, then lazily resolve them within handlers. Swapping adapters for testing stays easy.
Vercel deployment
Cron jobs hit `/api/hourly` every hour and `/api/daily` shortly after midnight UTC. Secrets live in Vercel, and Neon's schema auto-initialises when the service boots.
Observability
Structured JSON logging captures request IDs, latency, cache hits, and upstream errors—enough to wire alerts for repeated failures or missing daily posts.
Secrets & env matrix
Environment docs outline the Slack token, OpenAI keys, Neon URL and tuning flags so staging, preview, and production stay in lockstep.