1<!DOCTYPE html><html lang="en"> <head>
1<script>
vendor: 455 bytes, lines 1-11
1 2 (function (w, d, s, l, i) { 3 w[l] = w[l] || []; 4 w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" }); 5 var f = d.getElementsByTagName(s)[0], 6 j = d.createElement(s), 7 dl = l != "dataLayer" ? "&l=" + l : ""; 8 j.async = true; 9 j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl; 10 f.parentNode.insertBefore(j, f); 11 })(window, document, "script", "dataLayer", "
11GTM-WCJNSWTS
vendor: 8 bytes, lines 11-12
11"); 12
12</script>
12<!--
12<script is:inline> 13 // The Termly consent tag enables gtag URL passthrough, which appends _gl 14 // to same-site links so the client id survives without cookies. gtag 15 // reads the value into memory at container load, so the parameter can be 16 // dropped from the address bar afterwards without losing the handoff. 17 (function () { 18 var stripped = false; 19 function stripLinkerParam() { 20 if (stripped) return; 21 stripped = true; 22 var url = new URL(window.location.href); 23 if (!url.searchParams.has("_gl")) return; 24 url.searchParams.delete("_gl"); 25 window.history.replaceState(null, "", url.toString()); 26 } 27 window.dataLayer = window.dataLayer || []; 28 var push = window.dataLayer.push; 29 window.dataLayer.push = function () { 30 for (var i = 0; i < arguments.length; i++) { 31 if (arguments[i] && arguments[i].event === "gtm.load") { 32 window.setTimeout(stripLinkerParam, 0); 33 } 34 } 35 return push.apply(this, arguments); 36 }; 37 window.setTimeout(stripLinkerParam, 4000); 38 })(); 39 </script>
39 -->
39<script>(function(){const apiKey = "phc_BZVoUAXORGgUxvJAwR75nn5Nl0kze20qf1Qs21giUUO"; 40const apiHost = "https://us.i.posthog.com"; 41 42 // -- 1. PostHog stub ------------------------------------------------------- 43 // Creates window.posthog as a lightweight queue. Any capture() / identify() 44 // calls made before posthog.init() are held here. They are replayed by the 45 // real library once init() is called (consent granted), or silently dropped 46 // when the page unloads (consent never granted / declined). 47 // 48 // The e.__SV guard also prevents the stub being re-initialised on Astro 49 // View Transition navigations where this is:inline script re-executes. 50 !(function (t, e) { 51 var o, n, p, r; 52 e.__SV || 53 ((window.posthog = e), 54 (e._i = []), 55 (e.init = function (i, s, a) { 56 function g(t, e) { 57 var o = e.split("."); 58 (2 == o.length && ((t = t[o[0]]), (e = o[1])), 59 (t[e] = function () { 60 t.push([e].concat(Array.prototype.slice.call(arguments, 0))); 61 })); 62 } 63 (((p = t.createElement("script")).type = "text/javascript"), 64 (p.crossOrigin = "anonymous"), 65 (p.async = !0), 66 (p.src = s.api_host + "/static/array.js"), 67 (r = t.getElementsByTagName("script")[0]).parentNode.insertBefore( 68 p, 69 r, 70 )); 71 var u = e; 72 for ( 73 void 0 !== a ? (u = e[a] = []) : (a = "posthog"), 74 u.people = u.people || [], 75 u.toString = function (t) { 76 var e = "posthog"; 77 return ( 78 "posthog" !== a && (e += "." + a), 79 t || (e += " (stub)"), 80 e 81 ); 82 }, 83 u.people.toString = function () { 84 return u.toString(1) + ".people (stub)"; 85 }, 86 o = 87 "capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys getNextSurveyStep onSessionId".split( 88 " ", 89 ), 90 n = 0; 91 n < o.length; 92 n++ 93 ) 94 g(u, o[n]);
95 e._i.push([i, s, a]); 96 }), 97 (e.__SV = 1)); 98 })(document, window.posthog || []); 99 100 // -- 2. PostHog initialisation + Termly consent wiring -------------------- 101 // PostHog is always initialised with cookieless_mode "on_reject" so that 102 // users who decline or never engage with the Termly banner are still counted 103 // via a privacy-preserving server-side hash (no cookies, no localStorage). 104 // Termly is loaded via GTM (async), so window.Termly is never available when 105 // this script runs. We poll until it appears, then check the stored state and 106 // subscribe to changes to drive opt_in_capturing / opt_out_capturing. 107 // 108 // window.__phConsentBound guards against Astro View Transitions re-running 109 // this is:inline script on every client-side navigation, which would create 110 // duplicate polls and call posthog.init() on an already-running instance. 111 if (apiKey && !window.__phConsentBound) { 112 window.__phConsentBound = true; 113 (function () { 114 // optedIn tri-state: null = pending (SDK initialised, awaiting Termly 115 // consent decision); true = analytics consent granted (full 116 // localStorage+cookie tracking); false = declined or never engaged 117 // (cookieless server-hash tracking via on_reject mode). 118 var optedIn = null; 119 120 // Guard 1 â Drop events missing distinct_id. 121 // 122 // PostHog ingestion rejects any event whose distinct_id is absent or 123 // empty at the capture_validation pipeline step. This can happen in a 124 // brief race window during SDK initialisation when the SDK's 125 // persistence has been cleared but the new anonymous device ID has not 126 // yet been written back. enableExceptionAutocapture makes the window 127 // especially relevant: the window.onerror handler is registered early in 128 // the SDK's init sequence, so a JS exception thrown by a GTM tag, chat 129 // widget, or browser extension during that window produces a $exception 130 // event with no distinct_id that then fails ingestion. Dropping such 131 // events here prevents the ingestion error regardless of which code path 132 // produced the empty identity. 133 function dropEventsWithoutDistinctId(event) { 134 if (!event) return null; 135 var distinctId = event.properties && event.properties.distinct_id; 136 if ( 137 distinctId === null || 138 distinctId === undefined || 139 distinctId === "" 140 ) { 141 return null; 142 } 143 return event; 144 } 145 146 // Guard 2 â Drop noisy, non-actionable JSON-parse exceptions before they 147 // reach PostHog error tracking. Both variants are thrown by third-party 148 // code (browser extensions / privacy add-ons, or a GTM-injected tag that 149 // calls JSON.parse on a raw Google Ads gclid) and surface unhandled 150 // through window.onerror. None originate from our own code â they land 151 // on every page type, including the third-party-proxied /docs/* â so 152 // there is nothing to fix in-app; we filter them here to stop the 153 // Signals inbox filling with unfixable, non-actionable reports: 154 // 155 // - Firefox: `SyntaxError: JSON.parse: ...` with no stack frames. With 156 // nothing to group on, every occurrence mints a brand-new issue. 157 // - WebKit (Safari / iOS): `SyntaxError: JSON Parse error: Unexpected 158 // identifier ...` carrying a single synthetic frame whose filename is 159 // the page URL. These group into one stable issue that keeps going 160 // quiet and reopening weeks later, pulling someone in to re-triage. 161 // 162 // The WebKit variant carries a frame, so we can't simply require zero 163 // frames. To avoid swallowing genuine parse bugs in our own inline 164 // scripts, we require its single frame to point at the document URL 165 // itself â an external bundled script would report its own .js filename. 166 function onlyFrameIsDocument(frames) { 167 if (frames.length !== 1) return false; 168 var filename = frames[0] && (frames[0].filename || frames[0].abs_path); 169 if (!filename) return false;
170 var stripQuery = function (url) { 171 // The offending URL carries the gclid as a query param, so compare on 172 // origin + path only (both sides stripped of query string and hash). 173 return String(url).split("?")[0].split("#")[0]; 174 }; 175 return stripQuery(filename) === stripQuery(window.location.href); 176 } 177 function dropExtensionJsonParseErrors(event) { 178 if (!event || event.event !== "$exception") return event; 179 var list = (event.properties && event.properties.$exception_list) || []; 180 var allNoise = 181 list.length > 0 && 182 list.every(function (ex) { 183 if (!ex || ex.type !== "SyntaxError") return false; 184 var value = (ex && ex.value) || ""; 185 var handled = ex && ex.mechanism && ex.mechanism.handled; 186 if (handled !== false) return false; 187 var frames = (ex && ex.stacktrace && ex.stacktrace.frames) || []; 188 189 // Firefox: JSON.parse noise arrives with no stack frames at all. 190 if (value.indexOf("JSON.parse:") !== -1 && frames.length === 0) { 191 return true; 192 } 193 194 // WebKit (Safari / iOS): one synthetic frame that must point at the 195 // document itself â see onlyFrameIsDocument. 196 if ( 197 value.indexOf("JSON Parse error: Unexpected identifier") !== -1 && 198 onlyFrameIsDocument(frames) 199 ) { 200 return true; 201 } 202 203 return false; 204 }); 205 return allNoise ? null : event; 206 } 207 208 // Compose both guards into a single before_send handler. 209 function beforeSend(event) { 210 if (dropEventsWithoutDistinctId(event) === null) return null; 211 return dropExtensionJsonParseErrors(event); 212 } 213 214 // PostHog is always initialised immediately with cookieless_mode 215 // "on_reject". In this mode the SDK starts in a *pending* state: no 216 // cookies, no localStorage writes, no event capture â until one of: 217 // opt_in_capturing() -> full tracking with localStorage+cookie 218 // opt_out_capturing() -> cookieless server-hash tracking (privacy-safe) 219 // Termly drives which path is taken; see handleConsentState below. 220 function initPostHog() { 221 posthog.init(apiKey, { 222 api_host: apiHost || "https://us.i.posthog.com", 223 cookieless_mode: "on_reject", 224 persistence: "localStorage+cookie", // used only after opt_in_capturing() 225 defaults: "2026-01-30", 226 before_send: beforeSend, 227 enableExceptionAutocapture: true, 228 loaded: function (ph) { 229 // The loaded callback is the authoritative place to apply consent 230 // state to the real SDK, because stub method calls queued before 231 // the SDK loads are NOT reliably replayed into on_reject mode's 232 // consent state machine. Explicitly set the correct state here 233 // for all three cases: 234 // 235 // optedIn === true â analytics accepted before SDK loaded; 236 // call opt_in_capturing() on the real SDK. 237 // optedIn === false â declined/pending before SDK loaded; 238 // call opt_out_capturing() (cookieless hash). 239 // optedIn === null â Termly hasn't resolved yet; PostHog stays 240 // in pending state until handleConsentState 241 // fires on the real SDK directly. 242 if (optedIn === true) { 243 ph.opt_in_capturing(); 244 } else if (optedIn === false) { 245 ph.opt_out_capturing(); 246 } 247 }, 248 }); 249 } 250 251 // Called on first load (getConsentState) and on every preference change 252 // (Termly.on "consent"). Handles all scenarios: 253 // - New visitor, banner pending -> cookieless hash (on_reject default) 254 // - New visitor accepts -> opt_in: full localStorage+cookie 255 // - Returning visitor (accepted) -> opt_in (consent in Termly cookie) 256 // - Visitor declines -> opt_out: cookieless hash tracking 257 // - Consent withdrawn -> opt_out: cookieless hash tracking 258 // - Consent re-granted -> opt_in (only when previously opted out) 259 // - Other category changes ->
259 no-op (analytics state unchanged) 260 // 261 // NOTE: Termly.on("consent", handler) fires with payload 262 // {categories, consentState, cookies, uuid} 263 // whereas getConsentState() returns 264 // {analytics, advertising, ...} directly. 265 // We normalise both shapes here so one function handles both call sites. 266 function handleConsentState(state) { 267 var consentState = 268 state && state.consentState ? state.consentState : state; 269 if (consentState && consentState.analytics) { 270 // Analytics consent granted -> full tracking with cookies. 271 // Guard prevents spurious opt_in_capturing() calls when other Termly 272 // categories change while analytics consent remains granted. 273 if (optedIn !== true) { 274 optedIn = true; 275 posthog.opt_in_capturing(); 276 } 277 } else { 278 // Analytics declined, never engaged, or Termly state unavailable -> 279 // cookieless server-hash tracking. opt_out_capturing() halts 280 // cookie/localStorage use; no stored identity is retained. 281 if (optedIn !== false) { 282 optedIn = false; 283 posthog.opt_out_capturing(); 284 } 285 } 286 } 287 288 function attachTermly() { 289 // Synchronously check stored consent (handles returning visitors whose 290 // preference cookie is already set when Termly loads). 291 handleConsentState(window.Termly.getConsentState()); 292 // Subscribe to banner resolution and preference-centre changes. 293 if (typeof window.Termly.on === "function") { 294 window.Termly.on("consent", handleConsentState); 295 } 296 } 297 298 // Initialise PostHog immediately with cookieless_mode "on_reject". 299 // The SDK starts in pending state (no cookies, no capture) and waits 300 // for an explicit opt_in_capturing() or opt_out_capturing() call, which 301 // Termly drives below. Initialising here (rather than inside 302 // attachTermly) ensures the SDK is ready the moment Termly resolves, 303 // and means users who decline or never engage are still counted via the 304 // privacy-preserving server-side hash. 305 initPostHog(); 306 307 // Termly is loaded via GTM (async) so it will never be present when 308 // this script runs. Poll for window.Termly for up to 10 s. 309 // On timeout: fall back to cookieless hash tracking so users whose 310 // Termly never loads (ad-blocker, network error) are still counted. 311 // 50 ms x 200 attempts = 10 s ceiling. 312 var attempts = 0; 313 var poll = setInterval(function () { 314 if ( 315 window.Termly && 316 typeof window.Termly.getConsentState === "function" 317 ) { 318 clearInterval(poll); 319 attachTermly(); 320 } else if (++attempts >= 200) { 321 clearInterval(poll); 322 // Termly never loaded â enable cookieless hash tracking so these 323 // visitors are still counted without storing any personal data. 324 if (optedIn === null) { 325 optedIn = false; 326 posthog.opt_out_capturing(); 327 } 328 } 329 }, 50); 330 })(); 331 } 332})();</script>
332<meta name="theme-color" content="#2b215b"><meta name="zd-site-verification" content="7t9sz8mmjc4h40v4nj39ra"><!-- Preload critical fonts (self-hosted, same-origin) --><link rel="preload" href="/fonts/league-gothic/league-gothic-latin.woff" as="font" type="font/woff" crossorigin> <link rel="preload" href="/fonts/roboto/roboto-normal-latin.woff2" as="font" type="font/woff2" crossorigin> <!-- Roboto Condensed â used by CaseStudyHero and article headings --> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@300;400;500;600;700&display=swap"> <!-- Resource Hints --> <!-- preconnect pre-warms DNS + TCP + TLS for the heaviest third parties --> <link rel="preconnect" href="https://www.googletagmanager.com"> <link rel="preconnect" href="https://app.termly.io" crossorigin> <!-- go.astronomer.io is Marketo's Forms2 endpoint; pre-warming TCP+TLS reduces 333 form load latency, particularly on campaign-blast days when Marketo is slow. --> <link rel="preconnect" href="https://go.astronomer.io"> <!-- dns-prefetch as fallback for browsers that don't support preconnect, and for lower-priority origins --> <link rel="dns-prefetch" href="https://www.googletagmanager.com"> <link rel="dns-prefetch" href="https://app.termly.io"> <link rel="dns-prefetch" href="https://go.astronomer.io"> <!-- Global Metadata --> <meta name="google-site-verification" content="uI5eT7_d5Q5FjDUVa_VUxgeZxuBrYWiPMCLqZUOjrcA"> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <link rel="icon" type="image/svg+xml" href="/favicon.svg"> <meta name="generator" content="Astro v7.3.3"> <link rel="sitemap" href="/sitemap-index.xml"> <!-- Canonical URL --> <link rel="canonical" href="https://www.astronomer.io/product/"> <!-- Hreflang Alternate Links for SEO --> <!-- Primary Meta Tags --> <title>Astro: Enterprise-Grade Airflow for Scalable Data Pipelines</title> <meta name="title" content="Astro: Enterprise-Grade Airflow for Scalable Data Pipelines"> <meta name="description" content="Astro: Enterprise-grade Apache Airflow® for scalable, secure, and reliable data pipelines. Reduce downtime, improve data security, and boost ROI."> <meta name="robots" content="all"> <!-- Content ID --> <meta name="contentID" content="Page-product"> <meta name="contentType" content="Page" data-pagefind-filter="category[content]"> <!-- Open Graph / Facebook --> <meta property="og:type" content="website"> <meta property="og:url" content="https://www.astronomer.io/product/"> <meta property="og:title" content="Astro: Enterprise-Grade Airflow for Scalable Data Pipelines"> <meta property="og:description" content="Astro: Enterprise-grade Apache Airflow® for scalable, secure, and reliable data pipelines. Reduce downtime, improve data security, and boost ROI."> <meta property="og:image" content="/images/featured-image-homepage.jpg"> <!-- Twitter --> <meta property="twitter:card" content="summary_large_image"> <meta property="twitter:url" content="https://www.astronomer.io/product/"> <meta property="twitter:title" content="Astro: Enterprise-Grade Airflow for Scalable Data Pipelines"> <meta property="twitter:description" content="Astro: Enterprise-grade Apache Airflow® for scalable, secure, and reliable data pipelines. Reduce downtime, improve data security, and boost ROI."> <meta property="twitter:image" content="/images/featured-image-homepage.jpg"> <meta property="twitter:site" content="@astronomerio"> <meta property="twitter:creator" content="The Astronomer Team">
333<script type="application/ld+json">{ 334 "@context": "https://schema.org", 335 "@type": "WebPage", 336 "mainEntityOfPage": { 337 "@type": "WebPage", 338 "@id": "https://www.astronomer.io/product/" 339 }, 340 "headline": "Astro: Enterprise-Grade Airflow for Scalable Data Pipelines", 341 "description": "Astro: Enterprise-grade Apache Airflow® for scalable, secure, and reliable data pipelines. Reduce downtime, improve data security, and boost ROI.", 342 "image": { 343 "@type": "ImageObject", 344 "url": "undefined", 345 "width": "", 346 "height": "" 347 }, 348 "author": { 349 "@type": "Organization", 350 "name": "Astronomer" 351 }, 352 "publisher": { 353 "@type": "Organization", 354 "name": "Astronomer", 355 "logo": { 356 "@type": "ImageObject", 357 "url": "https://www.astronomer.io/logo.svg", 358 "width": "400", 359 "height": "41" 360 } 361 }, 362 "datePublished": "", 363 "@graph": [ 364 { 365 "@type": "Organization", 366 "@id": "https://www.astronomer.io/#organization", 367 "name": "Astronomer", 368 "url": "https://www.astronomer.io", 369 "logo": { 370 "@type": "ImageObject", 371 "url": "https://www.astronomer.io/logo.svg", 372 "width": "400", 373 "height": "41" 374 }, 375 "sameAs": [ 376 "https://github.com/astronomer", 377 "https://twitter.com/astronomerio", 378 "https://www.linkedin.com/company/astronomer/", 379 "https://www.facebook.com/astronomerio", 380 "https://www.youtube.com/c/Astronomer", 381 "https://www.instagram.com/astronomerdata/" 382 ], 383 "contactPoint": [ 384 { 385 "@type": "ContactPoint", 386 "url": "https://www.astronomer.io/contact/", 387 "email": "[email protected]", 388 "contactType": "company contact" 389 }, 390 { 391 "@type": "ContactPoint", 392 "url": "https://support.astronomer.io/", 393 "email": "[email protected]", 394 "contactType": "customer support" 395 }, 396 { 397 "@type": "ContactPoint", 398 "url": "https://www.astronomer.io/contact/", 399 "email": "[email protected]", 400 "contactType": "sales" 401 } 402 ], 403 "description": "Astronomer empowers data teams to enhance their data availability with Astro, a modern data orchestration platform powered by Apache Airflow. Astro enables companies to place Airflow at the core of their data operations, providing ease of use, scalability, and enterprise-grade security to ensure the reliable delivery of mission-critical data pipelines.", 404 "address": { 405 "@type": "PostalAddress", 406 "streetAddress": "54 W. 21st St., Suite 1100", 407 "addressLocality": "New York", 408 "addressRegion": "NY", 409 "postalCode": "10010", 410 "addressCountry": "US" 411 } 412 }, 413 { 414 "@type": "WebSite", 415 "@id": "https://www.astronomer.io/", 416 "url": "https://www.astronomer.io/", 417 "name": "Astronomer", 418 "publisher": { "@id": "https://www.astronomer.io/#organization" } 419 } 420 ] 421}</script>
421<link rel="preload" href="/pagefind/pagefind-component-ui.css" as="style" onload="this.onload=null;this.rel='stylesheet'"><noscript><link href="/pagefind/pagefind-component-ui.css" rel="stylesheet"></noscript>
421<script src="/pagefind/pagefind-component-ui.js" type="module"></script>
421<link rel="stylesheet" href="/_astro/BaseLayout.COSHb4xh.css"><style>.acds-hero{--acds-hero-height:100vh;--acds-hero-max-height:700px;--acds-hero-padding-top:214px;--acds-hero-padding-bottom:136px;height:var(--acds-hero-height);min-height:min-content;max-height:max-content;padding-top:var(--acds-hero-padding-top);padding-bottom:var(--acds-hero-padding-bottom);background:var(--background,#fff);color:var(--textColor,var(--moonshot-700));height:max-content;position:relative;overflow:clip}.acds-hero__content{--container-width:1210px;z-index:1;height:max-content;padding-bottom:var(--acds-hero-content-padding-bottom);opacity:1;position:relative}@keyframes hero-content-fadeIn{0%{opacity:0}to{opacity:1}} 422.acds-hero[data-astro-cid-pugaqhis]{background:var(--background,#fff);color:var(--textColor,var(--moonshot-700));height:max-content}.acds-hero__content[data-astro-cid-pugaqhis]{z-index:1;position:relative}.acds-hero__stars-bg[data-astro-cid-pugaqhis]{z-index:0;width:1245px;min-width:1245px;height:516px;margin:0 auto;position:absolute;top:0;left:0;right:0}.acds-hero__stars-bg-2[data-astro-cid-pugaqhis]{left:unset;width:auto;margin:0;right:calc(50% - 1200px)} 423</style><link rel="stylesheet" href="/_astro/SignupForm.60h7V7Es.css"><style>.page-section{background-color:var(--new-moon-95,#111121);color:#fff}.page-section+.page-section{padding-top:60px}.page-content-banner{--card-border-radius:5px}.page-content-banner__content{justify-content:space-evenly}.page-content-banner a{color:var(--new-moon-30);justify-self:end}.page-content-banner__title h2{font-size:20px;font-style:normal;font-weight:600;line-height:1.4}.page-stats-content{background:var(--new-moon-05);border-radius:20px;padding:108px 58px}.page-stat{--card-border-radius:5px;--card-background-color:var(--new-moon-10);--card-border-color:transparent;--card-text-color:var(--new-moon-70);text-align:center;flex-direction:column;justify-content:center;align-self:stretch;align-items:center;gap:10px;min-height:197px;display:flex}.page-stat__number{color:var(--new-moon-80,#343247);font-family:var(--theme-font-family-heading);text-transform:uppercase;font-size:72px;font-style:normal;font-weight:400;line-height:48px;transition:color .3s}.page-stat:hover{--card-background-color:var(--new-moon-20)}.page-stat:hover .page-stat__number{color:var(--purple-60)}.page-feature-stack__items{padding-top:38px;padding-bottom:60px}.page-feature-stack__item{--card-background-color:var(--new-moon-95);border-top:0;border-left:0;border-right:0;padding:48px 24px}.page-feature-stack__item:last-of-type{border-bottom:0}.page-feature-stack__item-text{--row-flow-margin:10px;max-width:384px}.page-feature-stack__item-link{margin-top:38px;display:block}#trusted{padding-top:120px}.acds-hero__image{margin-block:4rem}#trusted .acds-body-lg{margin-top:1rem}.acds-hero .acds-tag img{display:none}.otto-dot{background:var(--gold-40);border:1px solid #ffb32d40;border-radius:50%;width:7.5px;height:7.5px;animation:2s infinite pulse;display:inline-block}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}} 424.acds-calloutBox{text-align:center;border:1px solid var(--Purple-50,#a067fb);backdrop-filter:blur(2px);max-width:870px;color:var(--new-moon-10);background:#34324799;border-radius:5px;flex-direction:column;flex-shrink:0;justify-content:center;align-self:stretch;align-items:center;gap:28px;margin-inline:auto;padding:48px;display:flex}.acds-calloutBox__eyebrow{color:var(--purple-50,#a067fb);text-transform:uppercase;letter-spacing:.1em}.acds-calloutBox__title{color:var(--white)}.acds-calloutBox__title-highlight{color:var(--purple-50,#a067fb)}.acds-calloutBox__desc{max-width:740px}.acds-calloutBox__secondary-desc{color:var(--new-moon-30,#d0c9c2);max-width:740px}.acds-calloutBox .acds-button.purple{--button-background-color:var(--purple-50,#a067fb);--button-border-color:var(--purple-50,#a067fb);--button-background-color-hover:var(--purple-60,#872ded);--button-border-color-hover:var(--purple-60,#872ded);border-radius:10px} 425</style><link rel="stylesheet" href="/_astro/NebulaCallToAction.B2NgiwXI.css"><style>.acds-featureSection{--row-flow-margin:42px}.acds-featureSection,.acds-featureSection__children{z-index:1;position:relative}.acds-featureSection--bg-texture-image{top:calc(anchor(top) - 10px);z-index:0;pointer-events:none;width:1580px;max-width:1580px;height:726px;position:fixed;left:50%;transform:translate(-50%)}.acds-featureSection--bg-texture-image[src="/images/bg-textures/radial-bg-texture.svg"],.acds-featureSection--bg-texture-image[src="/images/bg-textures/bg-texture-architecture.svg"]{top:calc(anchor(top) + 10px);right:calc(anchor(right));z-index:0;pointer-events:none;width:1340px;height:1556px}.acds-featureSection__fixedTitle .acds-featureSection__fixedTitleContent{position:relative;top:10px}@media screen and (width<=979px){.page-cards .acds-featureSection__children .grid-container{grid-template-columns:1fr 1fr}.acds-card.acds-featureCard{max-width:100%}}@media screen and (width<=500px){.page-cards .acds-featureSection__children .grid-container{grid-template-columns:1fr}} 426</style><link rel="stylesheet" href="/_astro/Sections.InTo9sM7.css">
426<link rel="stylesheet" href="/_astro/FeatureTabs.Dj5CD7MT.css"><style>.acds-product-card{--card-border-radius:5px}.acds-product-card[data-theme-color=teal]{--card-border-color:var(--teal-50)}.acds-product-card[data-theme-color=teal] a,.acds-product-card[data-theme-color=teal] .acds-eyebrow{color:var(--teal-50)}.acds-product-card[data-theme-color=green]{--card-border-color:var(--green-50)}.acds-product-card[data-theme-color=green] a,.acds-product-card[data-theme-color=green] .acds-eyebrow{color:var(--green-50)}.acds-product-card[data-theme-color=red]{--card-border-color:var(--red-50)}.acds-product-card[data-theme-color=red] a,.acds-product-card[data-theme-color=red] .acds-eyebrow{color:var(--red-50)}.acds-product-card__items{flex-wrap:wrap;gap:24px;display:flex}.acds-product-card-one-image .acds-product-card__items{grid-template-columns:1fr 1fr;display:grid}.acds-product-card__item{--row-flow-margin:10px;--card-background-color:transparent;--card-border-color:transparent;flex:272px;padding:24px}.acds-product-card__item:hover,.acds-product-card__item:focus{background-color:var(--new-moon-95)}.acds-product-card__item-description{color:var(--new-moon-10,#f0ece5)}.acds-product-card__image{background:var(--new-moon-70,#555261);width:222px;height:222px}.acds-product-card-one-image .acds-product-card__image{background:0 0;width:100%;height:643px}.acds-product-card__link{margin-top:50px;display:block}.acds-product-card-one-image{min-height:max-content;padding-top:80px;padding-right:0;overflow:hidden}@media screen and (width<=980px){.acds-product-card__text{--row-flow-margin:10px}.acds-product-card__link{margin-top:1rem}.acds-product-card-one-image .acds-product-card__items{grid-template-columns:1fr}} 427.acds-faqsblock{--acds-faqsblock-background:var(--new-moon-90);background:var(--acds-faqsblock-background);flex-wrap:wrap;justify-content:center;align-items:flex-start;gap:165px;padding:80px 120px;display:flex;position:relative}.acds-faqsblock__title:before{content:"";stroke-dashoffset:6px;align-self:anchor-center;z-index:0;position-anchor:--acds-faqsblock__title;border-top:3px dashed #555261;width:1000%;position:absolute;left:-10rem;right:-10rem}.acds-faqsblock__title{anchor-name:--acds-faqsblock__title;anchor-scope:--acds-faqsblock__title}.acds-faqsblock__heading{background:var(--acds-faqsblock-background);z-index:2;font-family:var(--theme-font-family-heading), "League Gothic", "Albert Sans", sans-serif;letter-spacing:.02em;text-transform:uppercase;color:var(--white);margin:0;padding-inline:20px;font-size:60px;font-weight:400;line-height:68px;position:relative}.acds-faqsblock__description{color:var(--new-moon-10);margin-top:16px;font-family:Roboto,Inter,sans-serif;font-size:16px;line-height:1.75}.acds-faqsblock__faqs{flex:632px;max-width:632px}@media (width<=1024px){.acds-faqsblock{gap:32px;padding:60px 40px}.acds-faqsblock__title{flex:none}.acds-faqsblock__faqs{max-width:100%}}@media (width<=768px){.acds-faqsblock{flex-direction:column;gap:24px;padding:40px 24px}.acds-faqsblock__heading{font-size:48px;line-height:56px}}.page-faqs{padding-top:80px;padding-bottom:80px;overflow:hidden}.page-faqs .page-section__content{padding:0} 428.acds-accordion{background:var(--new-moon-95);border:2px solid var(--new-moon-70);box-sizing:border-box;margin-bottom:-4px;padding:20px;transition:background .3s;position:relative}.acds-accordion[open]{background:var(--new-moon-90)}.acds-accordion summary:after{display:none!important}.acds-accordion__summary::-webkit-details-marker{display:none}.acds-accordion__summary{cursor:pointer;color:var(--white);outline:none;justify-content:space-between;align-items:flex-start;gap:16px;margin:0;padding:0;list-style:none;display:flex}.acds-accordion__summary:focus-visible{outline:3px solid var(--purple-40);outline-offset:2px}.acds-accordion__question{letter-spacing:0;color:var(--white);font-variation-settings:"wdth" 75;flex:1;font-family:Roboto,sans-serif;font-size:20px;font-weight:600;line-height:28px}.acds-accordion__arrow{width:16px;height:16px;color:var(--white);flex-shrink:0;margin-top:6px;transition:transform .3s}.acds-accordion[open]>.acds-accordion__summary .acds-accordion__arrow{transform:rotate(180deg)}.acds-accordion__details-wrapper{box-sizing:border-box;overflow:hidden}.acds-accordion[data-closing] .acds-accordion__details-wrapper{display:block!important}.acds-accordion__details{letter-spacing:0;color:var(--new-moon-10);box-sizing:border-box;padding-top:10px;font-family:Roboto Mono,monospace;font-size:12px;font-weight:400;line-height:20px}.acds-accordion__details p{margin:0}.acds-accordion__details p+p{margin-top:12px}.acds-accordion__details p:last-child{margin-bottom:0}.acds-accordion__details a{color:var(--gold-40);text-decoration:underline}.acds-accordion__details a:hover{color:var(--gold-30)}.acds-accordion__details ul,.acds-accordion__details ol{margin:12px 0;padding-left:24px}.acds-accordion__details li{margin:4px 0}.acds-accordion__details code{background:var(--new-moon-95);border-radius:3px;padding:2px 6px;font-family:JetBrains Mono,monospace;font-size:11px}.acds-accordion__details pre{background:var(--new-moon-95);border-radius:5px;margin:12px 0;padding:12px;overflow-x:auto}.acds-accordion__details pre code{background:0 0;padding:0}@media (width<=768px){.acds-accordion{padding:16px}.acds-accordion__question{font-size:18px;line-height:26px}.acds-accordion__details{font-size:11px;line-height:18px}} 429.grid-container{gap:var(--grid-container-gap,1rem);align-items:var(--align-items,stretch);justify-content:var(--justify-content,start);display:grid}@media screen and (width>=980px){.grid-container{--grid-container-gap:var(--gap,3rem);grid-template-columns:var(--columnsManual,repeat(var(--columns,1), var(--columnWidth,1fr)))}}.grid-container.align-stretch{align-items:stretch}.grid-container.align-start{align-items:start}.grid-container.align-end{align-items:end}.grid-container.align-center{align-items:center}
429.grid-container.align-baseline{align-items:baseline}.grid-container.justify-start{justify-content:start}.grid-container.justify-end{justify-content:end}.grid-container.justify-center{justify-content:center}.grid-container.justify-stretch{justify-content:stretch}.grid-container.justify-space-around{justify-content:space-around}.grid-container.justify-space-between{justify-content:space-between}.grid-container.justify-space-evenly{justify-content:space-evenly} 430</style><link rel="stylesheet" href="/_astro/RuntimeArchitecture.DQtxwxV4.css"><link rel="stylesheet" href="/_astro/LogoGrid.OYsBc7YR.css"><link rel="stylesheet" href="/_astro/Tabs.BqkEiG_j.css"></head> <body>
vendor: 69 bytes, line 430
430 <noscript> <iframe src="https://www.googletagmanager.com/ns.html?id=
430GTM-WCJNSWTS
vendor: 84 bytes, line 430
430" height="0" width="0" style="display:none;visibility:hidden"></iframe> </noscript>
430<div class="header-wrapper"> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style>
430<script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).load=e;window.dispatchEvent(new Event("astro:load"));})();</script>
430<script>(()=>{var g=Object.defineProperty;var w=(a,s,c)=>s in a?g(a,s,{enumerable:!0,configurable:!0,writable:!0,value:c}):a[s]=c;var l=(a,s,c)=>w(a,typeof s!="symbol"?s+"":s,c);var E=new Set(["__proto__","constructor","prototype"]);{let a={0:t=>y(t),1:t=>c(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(c(t)),5:t=>new Set(c(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>Number.POSITIVE_INFINITY*t},s=t=>{let[p,e]=t;return p in a?a[p](e):void 0},c=t=>t.map(s),y=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([p,e])=>[p,s(e)]));class f extends HTMLElement{constructor(){super(...arguments);l(this,"Component");l(this,"hydrator");l(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let r=this.querySelectorAll("astro-slot"),n={},d=this.querySelectorAll("template[data-astro-template]");for(let o of d){let i=o.closest(this.tagName);i!=null&&i.isSameNode(this)&&(n[o.getAttribute("data-astro-template")||"default"]=o.innerHTML,o.remove())}for(let o of r){let i=o.closest(this.tagName);i!=null&&i.isSameNode(this)&&(n[o.getAttribute("name")||"default"]=o.innerHTML)}let u;try{u=this.hasAttribute("props")?y(JSON.parse(this.getAttribute("props"))):{}}catch(o){let i=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(i+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${i}`,this.getAttribute("props"),o),o}let h;await this.hydrator(this)(this.Component,u,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});l(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),r.disconnect(),this.childrenConnectedCallback()},r=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});r.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}getRetryImportUrl(e){let r=new URL(e,document.baseURI);return r.searchParams.set("astro-retry",Date.now().toString()),r.toString()}async importWithRetry(e){try{return await import(e)}catch(r){return await new Promise(n=>setTimeout(n,1e3)),import(this.getRetryImportUrl(e))}}handleHydrationError(e){let r=this.getAttribute("component-url"),n=new CustomEvent("astro:hydration-error",{cancelable:!0,bubbles:!0,composed:!0,detail:{error:e,componentUrl:r}});this.dispatchEvent(n)&&console.error(`[astro-island] Error hydrating ${r}`,e)}async start(){let e=JSON.parse(this.getAttribute("opts")),r=this.getAttribute("client");if(Astro[r]===void 0){window.addEventListener(`astro:${r}`,()=>this.start(),{once:!0});return}try{await Astro[r](async()=>{let n=this.getAttribute("renderer-url");try{let[d,{default:u}]=await Promise.all([this.importWithRetry(this.getAttribute("component-url")),n?this.importWithRetry(n):Promise.resolve({default:()=>()=>{}})]),h=this.getAttribute("component-export")||"default";if(h.includes(".")){this.Component=d;for(let m of h.split(".")){if(E.has(m)||!this.Component||typeof this.Component!="object"&&typeof this.Component!="function"||!Object.hasOwn(this.Component,m))throw new Error(`Invalid component export path: ${h}`);this.Component=this.Component[m]}}else{if(E.has(h))throw new Error(`Invalid component export path: ${h}`);this.Component=d[h]}return this.hydrator=u,this.hydrate}catch(d){return this.handleHydrationError(d),()=>{}}},e,this)}catch(n){this.handleHydrationError(n)}}attributeChangedCallback(){this.hydrate()}}l(f,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",f)}})();</script>
430<astro-island uid="ZI7B7P" prefix="r8" component-url="/_astro/Header.BVeGOihb.js?dpl=6abd1c0e8e511d0008ca4db3" component-export="default" renderer-url="/_astro/client.CgkIogCC.js?dpl=6abd1c0e8e511d0008ca4db3" props="{"theme":[0],"promoBanner":[0],"hidePromoBanner":[0,false]}" ssr client="load" opts="{"name":"Header","value":true}" await-children><header class="header" data-theme="dark"><div class="container flex-container justify-between headerWrapper"><a href="/" class="logo" aria-label="Go to Home Page"><span class="logoTitle">Astronomer</span><span class="logoImg"></span></a><button class="menuToggle" id="menuToggle" aria-label="Open Navigation Menu"><div class="hamburger hamburgerCollapse"><div class="hamburgerBox"><div class="hamburgerInner"></div></div></div></button><nav aria-label="Main Navigation" class="headerNav"><ul class="headerNavList list-reset"><li class="dropdown"><button class="block dropdownTitle">Product<svg width="14" height="15" viewBox="0 0 14 15" class="dropdown-caret" xmlns="http://www.w3.org/2000/svg"><path d="M3.74141 5.75L10.2584 5.75C10.3738 5.75002 10.4865 5.78425 10.5824 5.84835C10.6783 5.91245 10.7531 6.00354 10.7972 6.11012C10.8414 6.21669 10.8529 6.33396 10.8304 6.4471C10.8079 6.56024 10.7524 6.66417 10.6708 6.74575L7.41233 10.0042C7.30294 10.1136 7.15459 10.175 6.99991 10.175C6.84523 10.175 6.69689 10.1136 6.5875 10.0042L3.329 6.74575C3.24744 6.66417 3.1919 6.56024 3.16941 6.4471C3.14691 6.33396 3.15846 6.21669 3.2026 6.11012C3.24674 6.00354 3.32149 5.91245 3.41739 5.84835C3.5133 5.78425 3.62606 5.75002 3.74141 5.75Z"></path></svg></button><ul class="dropdownMenu list-reset is-two-column" id="product-dropdown"><li class="child hasChildren list-reset"><ul class="grandchildren list-reset product 431 "><li class="grandchild-section-title"><span class="topTitle">choose your deployment</span></li><li class="grandchild grandchild--hasDescription "><a href="/product/" class="link"><span class="icon-wrapper"><img src="/icons/nav/rocket-lunch (3).svg" data-gold-icon="/icons/nav/rocket-lunch (3)--gold.svg" width="24" height="24" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/rocket-lunch (3)--gold.svg" width="24" height="24" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">Astro</span><span class="linkDesc">Fully-managed orchestration platform, powered by Apache Airflow.</span></span></a></li><li class="grandchild grandchild--hasDescription "><a href="/product/astro-private-cloud/" class="link"><span class="icon-wrapper"><img src="/icons/nav/fi-br-cloud.svg" data-gold-icon="/icons/nav/fi-br-cloud--gold.svg" width="24" height="24" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/fi-br-cloud--gold.svg" width="24" height="24" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">Astro Private Cloud</span><span class="linkDesc">Run Airflow-as-a-service in your environment.</span></span></a></li><li class="grandchild-section-title"><span class="topTitle">agentic data engineering</span></li><li class="grandchild grandchild--hasDescription isHighlighted"><a href="/otto/" class="link"><span class="icon-wrapper"><img src="/icons/nav/otto.svg" data-gold-icon="/icons/nav/otto--gold.svg" width="39" height="40" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/otto--gold.svg" width="39" height="40" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">Introducing Otto</span><span class="linkDesc">Your data engineering agent built for Airflow.</span></span></a></li></ul></li><li class="child hasChildren list-reset"><div class="link hasChildren"><span class="linkTitle topTitle">Tools</span></div><ul class="grandchildren two-column list-reset tools 432 "><li class="grandchild "><a href="/cosmos/" class="link"><span class="icon-wrapper"><img src="/icons/nav/dbt.svg" data-gold-icon="/icons/nav/dbt--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/dbt--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">Cosmos</span></span></a></li><li class="grandchild "><a href="/docs/astro/cli/overview" class="link"><span class="icon-wrapper"><img src="/icons/nav/terminal square.svg" data-gold-icon="/icons/nav/terminal square--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/terminal square--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">CLI</span></span></a></li><li class="grandchild "><a href="https://github.com/astronomer/agents" class="link" target="_blank"><span class="icon-wrapper"><img src="/icons/nav/fi-br-cube.svg" data-gold-icon="/icons/nav/fi-br-cube--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/fi-br-cube--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">Agents</span></span></a></li><li class="grandchild "><a href="https://github.com/astronomer/blueprint" class="link" target="_blank"><span class="icon-wrapper"><img src="/icons/nav/fi-br-cube.svg" data-gold-icon="/icons/nav/fi-br-cube--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/fi-br-cube--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">Blueprint</span></span></a></li><li class="grandchild isCTA grandchild--hasDescription "><a href="/airflow/state-of-airflow/" class="link"><span class="grandchild-text"><span class="linkTitle topTitle">Updates</span><span class="isCTA--title">The State of Airflow 2026</span><span class="linkDesc">See the insights from over 5,800 data practitioners in the full report. <span class="nav-item-ctaLinkText">Download Now â</span></span></span></a></li></ul></li></ul></li><li class="dropdown"><a href="/customers/" class="block dropdownTitle">Customers</a></li><li class="dropdown"><a href="/docs/" class="block dropdownTitle">Docs</a></li><li class="dropdown"><button class="block dropdownTitle">Resources<svg width="14" height="15" viewBox="0 0 14 15" class="dropdown-caret" xmlns="http://www.w3.org/2000/svg"><path d="M3.74141 5.75L10.2584 5.75C10.3738 5.75002 10.4865 5.78425 10.5824 5.84835C10.6783 5.91245 10.7531 6.00354 10.7972 6.11012C10.8414 6.21669 10.8529 6.33396 10.8304 6.4471C10.8079 6.56024 10.7524 6.66417 10.6708 6.74575L7.41233 10.
4320042C7.30294 10.1136 7.15459 10.175 6.99991 10.175C6.84523 10.175 6.69689 10.1136 6.5875 10.0042L3.329 6.74575C3.24744 6.66417 3.1919 6.56024 3.16941 6.4471C3.14691 6.33396 3.15846 6.21669 3.2026 6.11012C3.24674 6.00354 3.32149 5.91245 3.41739 5.84835C3.5133 5.78425 3.62606 5.75002 3.74141 5.75Z"></path></svg></button><ul class="dropdownMenu list-reset " id="resources-dropdown"><li class="child hasChildren list-reset"><div class="link hasChildren"><span class="linkTitle topTitle">Insights</span></div><ul class="grandchildren two-column list-reset insights 433 "><li class="grandchild "><a href="/blog/" class="link"><span class="icon-wrapper"><img src="/icons/nav/blog.svg" data-gold-icon="/icons/nav/blog--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/blog--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">Blog</span></span></a></li><li class="grandchild "><a href="/events/webinars/" class="link"><span class="icon-wrapper"><img src="/icons/nav/play-alt.svg" data-gold-icon="/icons/nav/play-alt--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/play-alt--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">Webinars</span></span></a></li><li class="grandchild "><a href="/resources/" class="link"><span class="icon-wrapper"><img src="/icons/nav/book-open-cover.svg" data-gold-icon="/icons/nav/book-open-cover--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/book-open-cover--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">Resource Library</span></span></a></li><li class="grandchild "><a href="/events/" class="link"><span class="icon-wrapper"><img src="/icons/nav/calendar-days.svg" data-gold-icon="/icons/nav/calendar-days--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/calendar-days--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">Events</span></span></a></li></ul></li><li class="child hasChildren list-reset"><div class="link hasChildren"><span class="linkTitle topTitle">Education</span></div><ul class="grandchildren two-column list-reset education 434 "><li class="grandchild "><a href="https://academy.astronomer.io/" class="link" target="_blank"><span class="icon-wrapper"><img src="/icons/nav/academy.svg" data-gold-icon="/icons/nav/academy--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/academy--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">Academy</span></span></a></li><li class="grandchild "><a href="/airflow/" class="link"><span class="icon-wrapper"><img src="/icons/nav/airflow.svg" data-gold-icon="/icons/nav/airflow--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-default"/><img src="/icons/nav/airflow--gold.svg" width="12" height="12" loading="lazy" alt="" class="icon-gold"/></span><span class="grandchild-text"><span class="linkTitle ">What is Airflow?</span></span></a></li></ul></li></ul></li><li class="dropdown"><button class="block dropdownTitle">Airflow<svg width="14" height="15" viewBox="0 0 14 15" class="dropdown-caret" xmlns="http://www.w3.org/2000/svg"><path d="M3.74141 5.75L10.2584 5.75C10.3738 5.75002 10.4865 5.78425 10.5824 5.84835C10.6783 5.91245 10.7531 6.00354 10.7972 6.11012C10.8414 6.21669 10.8529 6.33396 10.8304 6.4471C10.8079 6.56024 10.7524 6.66417 10.6708 6.74575L7.41233 10.0042C7.30294 10.1136 7.15459 10.175 6.99991 10.175C6.84523 10.175 6.69689 10.1136 6.5875 10.0042L3.329 6.74575C3.24744 6.66417 3.1919 6.56024 3.16941 6.4471C3.14691 6.33396 3.15846 6.21669 3.2026 6.11012C3.24674 6.00354 3.32149 5.91245 3.41739 5.84835C3.5133 5.78425 3.62606 5.75002 3.74141 5.75Z"></path></svg></button><ul class="dropdownMenu list-reset " id="airflow-dropdown"><li class="child hasChildren list-reset"><ul class="grandchildren list-reset 435 "><li class="grandchild "><a href="/airflow/" class="link"><span class="grandchild-text"><span class="linkTitle ">What is Airflow?</span></span></a></li><li class="grandchild "><a href="/airflow/3/" class="link"><span class="grandchild-text"><span class="linkTitle ">Airflow 3</span></span></a></li><li class="grandchild "><a href="/astro-vs-apache-airflow-oss/" class="link"><span class="grandchild-text"><span class="linkTitle ">Astro vs. Apache Airflow OSS</span></span></a></li><li class="grandchild ">
435<a href="/airflow/use-cases/" class="link"><span class="grandchild-text"><span class="linkTitle ">Airflow Use Cases</span></span></a></li><li class="grandchild "><a href="/airflow/security/" class="link"><span class="grandchild-text"><span class="linkTitle ">Airflow Security</span></span></a></li></ul></li></ul></li><li class="dropdown"><a href="/pricing/" class="block dropdownTitle">Pricing</a></li></ul><div class="flex-container buttons justify-center"><button class="search-button" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M15.7656 14.5469L12.3594 11.1406C13.3281 9.95312 13.875 8.48438 13.875 6.9375C13.875 5.10938 13.1562 3.39062 11.8594 2.09375C10.5625 0.796875 8.83594 0.078125 7.00781 0.078125C5.17969 0.078125 3.45312 0.796875 2.15625 2.09375C0.859375 3.39062 0.140625 5.10938 0.140625 6.9375C0.140625 8.76562 0.859375 10.4844 2.15625 11.7812C3.45312 13.0781 5.17188 13.7969 7 13.7969C8.54688 13.7969 10.0156 13.25 11.2031 12.2812L14.6094 15.6875C14.625 15.7031 14.6437 15.7156 14.6643 15.7243C14.6849 15.733 14.707 15.7377 14.7293 15.7381C14.7515 15.7385 14.7738 15.7346 14.7947 15.7266C14.8156 15.7187 14.8348 15.7068 14.8511 15.6917C14.8673 15.6765 14.8803 15.6583 14.8894 15.6381C14.8984 15.618 14.9034 15.5962 14.9039 15.5741C14.9045 15.5519 14.9007 15.5299 14.8927 15.5094C14.8847 15.4888 14.8727 15.4701 14.8574 15.4543L15.7656 14.5469ZM10.7031 10.625C9.6875 11.6406 8.38281 12.2031 7 12.2031C5.61719 12.2031 4.3125 11.6406 3.29688 10.625C2.28125 9.60938 1.71875 8.30469 1.71875 6.92188C1.71875 5.53906 2.28125 4.23438 3.29688 3.21875C4.3125 2.20312 5.61719 1.64062 7 1.64062C8.38281 1.64062 9.6875 2.20312 10.7031 3.21875C11.7188 4.23438 12.2812 5.53906 12.2812 6.92188C12.2812 8.30469 11.7188 9.60938 10.7031 10.625Z" fill="white"></path></svg></button><a href="https://cloud.astronomer.io/" class="link login-link">Log In</a><a class="acds-button secondary" href="/book-a-demo/">
435Book a Demo</a><a class="acds-button" href="/lp/signup/">Get Started Free</a></div></nav></div></header><!--astro:end--></astro-island> </div> <main data-pagefind-body> <div class="acds-hero" style="--background: var(--new-moon-95, #111121);;--textColor: #fff;" data-astro-cid-pugaqhis> <div class="acds-hero__content" style="--background: var(--new-moon-95, #111121);;--textColor: #fff;" data-astro-cid-pugaqhis> <div class="container acds-container text-center" style="--container-width:910px"> <div class="acds-hero__content row-flow"> <div class="acds-hero__text row-flow"> <a class="acds-tag flex-container text-center justify-center align-center" id="page-hero-tag" style="background:#18131A;color:#EFB34C;max-width:max-content;margin-inline:auto;gap:6px;border:1px solid #EFB34C" href="/otto/"> <img src="/icons/nav/otto--gold.svg" alt=""> <span "><div class='otto-dot'></div> Meet otto: the agent built for airflow</span> </a> <h1 class="acds-h1">Build, run, and observe your data pipelines.</h1> <p class="acds-body-xl">Astro is the unified orchestration platform for Apache Airflow®. Develop faster, scale reliably, and monitor everything in one place.</p> </div> <div class="acds-hero__buttons flex-container justify-center align-center"> <a href="/lp/signup/" class="acds-button fill"> Get Started Free </a><a href="/book-a-demo/" class="acds-button secondary"> Book a Demo </a> </div> </div> </div> </div> <div class="acds-hero__image container text-left" style="--container-width:910px;"> <a href="/product-tour/" style="display: block; position: relative; width: 100%;"> <img src="https://c.navattic.com/nv_static/61c0bf17635d069b816406e59ee453d64ea81f622309491126c5db365d79bd78" alt="Product Demo" style="width: 100%; height: 100%; object-fit: cover; border: none;"> <div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.7); color: white; padding: 12px 24px; border-radius: 8px; font-weight: 600;"> 436Explore Astro 437</div> </a> </div><div class="acds-hero__logos container" style="--container-width:1212px;"> <div class="logo-grid-wrapper"><div class="logo-grid"><div class="logo-grid__item"><img src="/images/customers/home/notion.svg" alt="Notion" loading="lazy"/></div><div class="logo-grid__item"><img src="/images/customers/home/wynn-resorts.svg" alt="Wynn Resorts" loading="lazy"/></div><div class="logo-grid__item"><img src="/images/customers/home/molson-coors.svg" alt="Molson Coors" loading="lazy"/></div><div class="logo-grid__item"><img src="/images/customers/home/together-ai.svg" alt="together.ai" loading="lazy"/></div><div class="logo-grid__item"><img src="/images/customers/home/t.rowe price.svg" alt="T.Rowe Price" loading="lazy"/></div><div class="logo-grid__item"><img src="/images/customers/home/molson coors.svg" alt="Molson Coors" loading="lazy"/></div><div class="logo-grid__item"><img src="/images/customers/home/merck.svg" alt="Merck" loading="lazy"/></div></div></div> </div> </div>
437<script>(()=>{var l=(n,t)=>{let i=async()=>{await(await n())()},e=typeof t.value=="object"?t.value:void 0,s={timeout:e==null?void 0:e.timeout};"requestIdleCallback"in window?window.requestIdleCallback(i,s):setTimeout(i,s.timeout||200)};(self.Astro||(self.Astro={})).idle=l;window.dispatchEvent(new Event("astro:idle"));})();</script>
437<astro-island uid="15o2AY" prefix="r6" component-url="/_astro/Sections.DsdE9-vS.js?dpl=6abd1c0e8e511d0008ca4db3" component-export="default" renderer-url="/_astro/client.CgkIogCC.js?dpl=6abd1c0e8e511d0008ca4db3" props="{"sections":[1,[[0,{"id":[0,"top"],"type":[0,"titleBlock"],"title":[0,"One place to manage the complete data pipeline lifecycle."],"description":[0,"Astro brings development, execution, and monitoring together so your team spends less time on infrastructure and more time shipping."],"titleAlignment":[0,"center"]}],[0,{"id":[0,"otto-banner"],"type":[0,"contentBanner"],"noTopTitle":[0,true],"title":[0,"Meet Otto, your data engineering agent"],"description":[0,"Accelerate every part of your workflow with Otto. Author Dags, review pull requests, investigate production failures, or scope your next upgrade. Otto has the context to act, not just advise, with knowledge of your task logs, connections, and run history, plus the Airflow expertise to know what to do with it."],"component":[0,"OttoIntroPrompt"],"link":[0,{"text":[0,"Learn more about Otto â"],"href":[0,"/product/otto/"]}]}],[0,{"id":[0,"build"],"type":[0,"productCard"],"eyebrow":[0,"Build"],"noTopTitle":[0,true],"oneImage":[0,true],"title":[0,"Ship production-ready pipelines in minutes."],"description":[0,"Astro's development toolkit covers your entire workflow with AI-assisted coding, flexible testing environments, automated CI/CD, and centralized configuration management."],"link":[0,{"text":[0,"Explore Build features â"],"href":[0,"/product/features#build"]}],"themeColor":[0,"teal"],"image":[0,{"src":[0,"/images/product/build.svg"],"alt":[0,""]}],"items":[1,[[0,{"title":[0,"Astro IDE"],"description":[0,"Browser-based editor with context-aware AI that generates production-ready Airflow code. Test and validate Dags instantly, then deploy with one click. No local setup required."],"image":[0,{"src":[0,""],"alt":[0,""]}]}],[0,{"title":[0,"Astro CLI"],"description":[0,"Run a complete local Airflow environment to build and test Dags with full control, then deploy to Astro with a single command."],"image":[0,{"src":[0,""],"alt":[0,""]}]}],[0,{"title":[0,"Astro Terraform Provider"],"description":[0,"Manage Astro infrastructure as code with Terraform. Provision workspaces, deployments, and clusters using version-controlled configurations. API access also available."],"image":[0,{"src":[0,""],"alt":[0,""]}]}],[0,{"title":[0,"<span style='text-transform:lowercase !important'>dbt</span> Orchestration"],"description":[0,"Native integration that turns dbt projects into Dags with model-level visibility."],"image":[0,{"src":[0,""],"alt":[0,""]}]}]]]}],[0,{"id":[0,"run"],"type":[0,"productCard"],"noTopTitle":[0,true],"oneImage":[0,true],"eyebrow":[0,"Run"],"title":[0,"Scale to any workload without managing infrastructure."],"description":[0,"The Astro engine delivers 2x the performance of other Airflow solutions. Auto-scaling, high availability, and multi-cloud deployment, all made easy for you."],"link":[0,{"text":[0,"Explore Run features â"],"href":[0,"/product/features#run"]}],"
437themeColor":[0,"green"],"image":[0,{"src":[0,"/images/product/run.svg"],"alt":[0,""]}],"items":[1,[[0,{"title":[0,"Elastic Auto-Scaling"],"description":[0,"Workers that scale up and down based on task queue depth. Pay only for what you use."],"image":[0,{"src":[0,""],"alt":[0,""]}]}],[0,{"title":[0,"Disaster Recovery"],"description":[0,"One-click cross-region failover with underlying data replication handled automatically."],"image":[0,{"src":[0,""],"alt":[0,""]}]}],[0,{"title":[0,"Remote Execution"],"description":[0,"Run sensitive workloads in your own environment and dedicated hardware. All while maintaining centralized orchestration and observability in Astro."],"image":[0,{"src":[0,""],"alt":[0,""]}]}],[0,{"title":[0,"High Availability"],"description":[0,"Multi-AZ deployments with automatic failover. 99.5% uptime SLA."],"image":[0,{"src":[0,""],"alt":[0,""]}]}]]]}],[0,{"id":[0,"observe"],"type":[0,"productCard"],"noTopTitle":[0,true],"oneImage":[0,true],"eyebrow":[0,"Observe"],"title":[0,"Detect issues before they impact downstream systems."],"description":[0,"Real-time lineage, SLA monitoring, proactive alerting, and AI-powered RCA, all built into the orchestration layer, not bolted on as a separate tool."],"link":[0,{"text":[0,"Explore Observe features â"],"href":[0,"/product/features#observe"]}],"image":[0,{"src":[0,"/images/product/observe.svg"],"alt":[0,""]}],"themeColor":[0,"red"],"items":[1,[[0,{"title":[0,"Pipeline Lineage"],"description":[0,"Trace upstream/downstream dependencies across Dags, tables, and teams."],"image":[0,{"src":[0,""],"alt":[0,""]}]}],[0,{"title":[0,"Data Product SLAs"],"description":[0,"Set freshness targets, track performance, and alert before deadlines miss."],"image":[0,{"src":[0,""],"alt":[0,""]}]}],[0,{"title":[0,"Proactive Alerts"],"description":[0,"Get notified in Slack, PagerDuty, or email the moment a Dag fails, runs long, or misses its schedule."],"image":[0,{"src":[0,""],"alt":[0,""]}]}],[0,{"title":[0,"AI-Assisted RCA"],"description":[0,"Get to the root cause faster with AI that pinpoints what failed and suggests fixesâwithout manual log analysis."],"image":[0,{"src":[0,""],"alt":[0,""]}]}]]]}],[0,{"id":[0,"enterprise-ready"],"type":[0,"contentBanner"],"noTopTitle":[0,true],"title":[0,"Enterprise-ready security and governance"],"description":[0,"Built for teams with strict compliance requirements"],"link":[0,{"text":[0,"View All â"],"href":[0,"/product/features#enterprise"]}],"content":[0,"<ul class='list-reset flex-container'><li class='flex-container' style='gap:14px'><svg xmlns='http://www.w3.org/2000/svg' width='15' height='10' viewBox='0 0 15 10' fill='none'><path d='M14.5787 0.247623C14.8499 0.531711 14.8707 0.96339 14.6475 1.27007L14.5524 1.37869L5.75233 9.77869C5.48183 10.0369 5.07555 10.0692 4.77111 9.87551L4.64757 9.77868L0.247617 5.57868C-0.0719796 5.27361 -0.0837529 4.76721 0.22132 4.44762C0.492496 4.16353 0.922741 4.12266 1.23947 4.33135L1.35238 4.42132L5.1984 8.0928L13.4476 0.221315C13.7672 -0.0837554 14.2736 -0.0719767 14.5787 0.247623Z' fill='#19BA5A'/></svg> SOC 2 Type II</li><li class='flex-container' style='gap:14px'><svg xmlns='http://www.w3.org/2000/svg' width='15' height='10'
437 viewBox='0 0 15 10' fill='none'><path d='M14.5787 0.247623C14.8499 0.531711 14.8707 0.96339 14.6475 1.27007L14.5524 1.37869L5.75233 9.77869C5.48183 10.0369 5.07555 10.0692 4.77111 9.87551L4.64757 9.77868L0.247617 5.57868C-0.0719796 5.27361 -0.0837529 4.76721 0.22132 4.44762C0.492496 4.16353 0.922741 4.12266 1.23947 4.33135L1.35238 4.42132L5.1984 8.0928L13.4476 0.221315C13.7672 -0.0837554 14.2736 -0.0719767 14.5787 0.247623Z' fill='#19BA5A'/></svg> HIPAA</li><li class='flex-container' style='gap:14px'><svg xmlns='http://www.w3.org/2000/svg' width='15' height='10' viewBox='0 0 15 10' fill='none'><path d='M14.5787 0.247623C14.8499 0.531711 14.8707 0.96339 14.6475 1.27007L14.5524 1.37869L5.75233 9.77869C5.48183 10.0369 5.07555 10.0692 4.77111 9.87551L4.64757 9.77868L0.247617 5.57868C-0.0719796 5.27361 -0.0837529 4.76721 0.22132 4.44762C0.492496 4.16353 0.922741 4.12266 1.23947 4.33135L1.35238 4.42132L5.1984 8.0928L13.4476 0.221315C13.7672 -0.0837554 14.2736 -0.0719767 14.5787 0.247623Z' fill='#19BA5A'/></svg> SSO/SCIM</li><li class='flex-container' style='gap:14px'><svg xmlns='http://www.w3.org/2000/svg' width='15' height='10' viewBox='0 0 15 10' fill='none'><path d='M14.5787 0.247623C14.8499 0.531711 14.8707 0.96339 14.6475 1.27007L14.5524 1.37869L5.75233 9.77869C5.48183 10.0369 5.07555 10.0692 4.77111 9.87551L4.64757 9.77868L0.247617 5.57868C-0.0719796 5.27361 -0.0837529 4.76721 0.22132 4.44762C0.492496 4.16353 0.922741 4.12266 1.23947 4.33135L1.35238 4.42132L5.1984 8.0928L13.4476 0.221315C13.7672 -0.0837554 14.2736 -0.0719767 14.5787 0.247623Z' fill='#19BA5A'/></svg> RBAC</li></ul>"]}],[0,{"id":[0,"enterprise-ready"],"type":[0,"stats"],"noTopTitle":[0,true],"title":[0,null],"description":[0,null],"items":[1,[[0,{"number":[0,"2.5x"],"description":[0,"Concurrent tasks vs. managed alternatives"]}],[0,{"number":[0,"1HR"],"description":[0,"Support response SLA"]}],[0,{"number":[0,"Day 0"],"description":[0,"Airflow 3 access"]}]]]}],[0,{"id":[0,"trusted"],"type":[0,"featureStack"],"noTopTitle":[0,true],"title":[0,"Trusted by data and AI teams."],"description":[0,"From ETL to LLM pipelines, teams use Astro to orchestrate critical workflows."],"items":[1,[[0,{"title":[0,"Everlane"],"logo":[0,"/images/logos/everlane.png"],"description":[0,"Everlane runs 70+ critical Dags on Astro powering revenue metrics, inventory tracking, and churn prediction to support daily and strategic decisions across finance, marketing, logistics, and retail teams."],"link":[0,{"text":[0,"Learn More â"],"href":[0,"/case-studies/everlane/"]}],"testimonial":[0,{"stat":[0,{"number":[0,"25%"],"text":[0,"Cost Reduction"]}],"quote":[0,"Astro gives us peace of mind. We donât worry about infrastructure. We focus on delivering data."],"name":[0,"Madhuri Gupta"],"jobTitle":[0,"Senior Manager of Data & Analytics"],"company":[0,"Everlane"],"href":[0,"/case-studies/everlane/"]}]}],[0,{"title":[0,"WeWork"],"description":[0,"WeWork's lean data team uses Astro to orchestrate data ingestion, transformation, and reporting pipelines globally, supporting decision-making across the business."],"link":[0,{"text":[0,"Learn More â"],"href":[0,"/case-studies/wework/"]}],"logo":[0,"/images/observe2/wework.png"],"testimonial":[0,{"stat":[0,{"number":[0,"67%"],"text":[0,"Infra Management Reduction"]}],"quote":[0,"Astronomer makes doing more with less possible. It's a massively important part of our stack, since everything we buildâmodels, APIs, workflowsâare running on top of it."],"name":[0,"You Zhou"],"jobTitle":[0,"Director of Data Engineering"],"company":[0,"WeWork"],"href":[0,"/case-studies/wework/"]}]}],[0,{"title":[0,"AAA Life Insurance"],"logo":[0,"/images/customers/new/aaa.png"],"description":[0,"AAA Life uses Astro to run dozens of production Dags including dbt transformations, reverse ETL to CRM systems, and S3 exports to deliver timely insights to 1.6 million policyholders."],"link":[0,{"text":[0,"Learn More â"],"href":[0,"/case-studies/aaalife/"]}],"testimonial":[0,{"stat":[0,{"number":[0,"80%"],"text":[0,"Faster Trouble-shooting"]}],"quote":[0,"The biggest impact that I'
437ve seen personally is us being able to recover from failure much quicker, and that has had a significant impact on us being able to meet our SLAs."],"name":[0,"Josh Bickmeyer"],"jobTitle":[0,"Manager, Analytics Engineering"],"company":[0,"AAA LIFE INSURANCE"],"href":[0,"/case-studies/aaalife/"]}]}]]]}]]]}" ssr client="idle" opts="{"name":"Sections","value":true}" await-children><div class="acds-sections-wrapper"><section id="top" class="page-section page-titleBlock"><div class="page-section__content container"><div class="acds-featureSection row-flow undefined" style="anchor-name:--featureSection-top"><div class="grid-container align-stretch justify-start" style="--columns:1;--columnWidth:1fr;--gap:12px;--columnsManual:1fr"><h2 class="acds-featureSection__title acds-h2 text-center">One place to manage the complete data pipeline lifecycle.</h2><div class="acds-featureSection__description text-center acds-body-lg">Astro brings development, execution, and monitoring together so your team spends less time on infrastructure and more time shipping.</div></div><div class="acds-featureSection__children"></div></div></div></section><section id="otto-banner" class="page-section page-contentBanner"><div class="page-section__content container"><div class="acds-featureSection row-flow undefined" style="anchor-name:--featureSection-otto-banner"><div class="acds-featureSection__children"><div class="page-content-banner acds-card"><div class="grid-container align-center justify-space-between" style="--columns:1;--columnWidth:1fr;--gap:3rem;--columnsManual:390px 660px"><div class="page-content-banner__title row-flow" style="--row-flow-margin:1rem"><h2 class="acds-body-xl acds-font-family-body acds-reset-text-transform">Meet Otto, your data engineering agent</h2><p class="acds-body-md">Accelerate every part of your workflow with Otto. Author Dags, review pull requests, investigate production failures, or scope your next upgrade. Otto has the context to act, not just advise, with knowledge of your task logs, connections, and run history, plus the Airflow expertise to know what to do with it.</p><a href="/product/otto/" class="acds-body-mono acds-button block">Learn more about Otto â</a></div><div class="page-content-banner__content flex-container"><div class="acds-oi-prompt" aria-hidden="true"></div></div></div></div></div></div></div></section><section id="build" class="page-section page-productCard"><div class="page-section__content container"><div class="acds-featureSection row-flow undefined" style="anchor-name:--featureSection-build"><div class="acds-featureSection__children"><div class="acds-card acds-product-card acds-product-card-one-image" data-theme-color="teal"><div class="grid-container align-center justify-center" style="--columns:1;--columnWidth:1fr;--gap:30px;--columnsManual:570px 1fr"><div class="acds-product-card__text row-flow"><p class="acds-product-card__eyebrow acds-eyebrow">Build</p><h2 class="acds-h2 acds-product-card__title">Ship production-ready pipelines in minutes.</h2><p class="acds-body-mono acds-product-card__description">Astro's development toolkit covers your entire workflow with AI-assisted coding, flexible testing environments, automated CI/CD, and centralized configuration management.</p><a href="/product/features#build" class="acds-product-card__link acds-body-mono">Explore Build features â</a><div class="acds-product-card__items"><div class="acds-card acds-product-card__item row-flow"><h3 class="acds-product-card__item-title acds-h5 acds-text-transform-reset"><span>Astro IDE</span></h3><p class="acds-product-card__item-desecription acds-body-sm">Browser-based editor with context-aware AI that generates production-ready Airflow code. Test and validate Dags instantly, then deploy with one click. No local setup required.</p></div><div class="acds-card acds-product-card__item row-flow"><h3 class="acds-product-card__item-title acds-h5 acds-text-transform-reset"><span>Astro CLI</span></h3><p class="acds-product-card__item-desecription acds-body-sm">Run a complete local Airflow environment to build and test Dags with full control, then deploy to Astro with a single command.</p></div><div class="acds-card acds-product-card__item row-flow"><h3 class="acds-product-card__item-title acds-h5 acds-text-transform-reset"><span>Astro Terraform Provider</span></h3><p class="acds-product-card__item-desecription acds-body-sm">
437Manage Astro infrastructure as code with Terraform. Provision workspaces, deployments, and clusters using version-controlled configurations. API access also available.</p></div><div class="acds-card acds-product-card__item row-flow"><h3 class="acds-product-card__item-title acds-h5 acds-text-transform-reset"><span><span style="text-transform:lowercase !important">dbt</span> Orchestration</span></h3><p class="acds-product-card__item-desecription acds-body-sm">Native integration that turns dbt projects into Dags with model-level visibility.</p></div></div></div><div class="acds-product-card__image"><img src="/images/product/build.svg" alt="" width="100%" height="100%" loading="lazy"/></div></div></div></div></div></div></section><section id="run" class="page-section page-productCard"><div class="page-section__content container"><div class="acds-featureSection row-flow undefined" style="anchor-name:--featureSection-run"><div class="acds-featureSection__children"><div class="acds-card acds-product-card acds-product-card-one-image" data-theme-color="green"><div class="grid-container align-center justify-center" style="--columns:1;--columnWidth:1fr;--gap:30px;--columnsManual:570px 1fr"><div class="acds-product-card__text row-flow"><p class="acds-product-card__eyebrow acds-eyebrow">Run</p><h2 class="acds-h2 acds-product-card__title">Scale to any workload without managing infrastructure.</h2><p class="acds-body-mono acds-product-card__description">The Astro engine delivers 2x the performance of other Airflow solutions. Auto-scaling, high availability, and multi-cloud deployment, all made easy for you.</p><a href="/product/features#run" class="acds-product-card__link acds-body-mono">Explore Run features â</a><div class="acds-product-card__items"><div class="acds-card acds-product-card__item row-flow"><h3 class="acds-product-card__item-title acds-h5 acds-text-transform-reset"><span>Elastic Auto-Scaling</span></h3><p class="acds-product-card__item-desecription acds-body-sm">Workers that scale up and down based on task queue depth. Pay only for what you use.</p></div><div class="acds-card acds-product-card__item row-flow"><h3 class="acds-product-card__item-title acds-h5 acds-text-transform-reset"><span>Disaster Recovery</span></h3><p class="acds-product-card__item-desecription acds-body-sm">One-click cross-region failover with underlying data replication handled automatically.</p></div><div class="acds-card acds-product-card__item row-flow"><h3 class="acds-product-card__item-title acds-h5 acds-text-transform-reset"><span>Remote Execution</span></h3><p class="acds-product-card__item-desecription acds-body-sm">Run sensitive workloads in your own environment and dedicated hardware. All while maintaining centralized orchestration and observability in Astro.</p></div><div class="acds-card acds-product-card__item row-flow"><h3 class="acds-product-card__item-title acds-h5 acds-text-transform-reset"><span>High Availability</span></h3><p class="acds-product-card__item-desecription acds-body-sm">Multi-AZ deployments with automatic failover. 99.5% uptime SLA.</p></div></div></div><div class="acds-product-card__image"><img src="/images/product/run.svg" alt="" width="100%" height="100%" loading="lazy"/></div></div></div></div></div></div></section><section id="observe" class="page-section page-productCard"><div class="page-section__content container"><div class="acds-featureSection row-flow undefined" style="anchor-name:--featureSection-observe"><div class="acds-featureSection__children"><div class="acds-card acds-product-card acds-product-card-one-image" data-theme-color="red"><div class="grid-container align-center justify-center" style="--columns:1;--columnWidth:1fr;--gap:30px;--columnsManual:570px 1fr"><div class="acds-product-card__text row-flow"><p class="acds-product-card__eyebrow acds-eyebrow">Observe</p><h2 class="acds-h2 acds-product-card__title">Detect issues before they impact downstream systems.</h2><p class="acds-body-mono acds-product-card__description">Real-time lineage, SLA monitoring, proactive alerting, and AI-powered RCA, all built into the orchestration layer, not bolted on as a separate tool.</p><a href="/product/features#observe" class="acds-product-card__link acds-body-mono">Explore Observe features â</a><div class="acds-product-card__items"><div class="acds-card acds-product-card__item row-flow"><h3 class="acds-product-card__item-title acds-h5 acds-text-transform-reset"><span>Pipeline Lineage</span></h3><p class="acds-product-card__item-desecription acds-body-sm">Trace upstream/downstream dependencies across Dags, tables, and team
437s.</p></div><div class="acds-card acds-product-card__item row-flow"><h3 class="acds-product-card__item-title acds-h5 acds-text-transform-reset"><span>Data Product SLAs</span></h3><p class="acds-product-card__item-desecription acds-body-sm">Set freshness targets, track performance, and alert before deadlines miss.</p></div><div class="acds-card acds-product-card__item row-flow"><h3 class="acds-product-card__item-title acds-h5 acds-text-transform-reset"><span>Proactive Alerts</span></h3><p class="acds-product-card__item-desecription acds-body-sm">Get notified in Slack, PagerDuty, or email the moment a Dag fails, runs long, or misses its schedule.</p></div><div class="acds-card acds-product-card__item row-flow"><h3 class="acds-product-card__item-title acds-h5 acds-text-transform-reset"><span>AI-Assisted RCA</span></h3><p class="acds-product-card__item-desecription acds-body-sm">Get to the root cause faster with AI that pinpoints what failed and suggests fixesâwithout manual log analysis.</p></div></div></div><div class="acds-product-card__image"><img src="/images/product/observe.svg" alt="" width="100%" height="100%" loading="lazy"/></div></div></div></div></div></div></section><section id="enterprise-ready" class="page-section page-contentBanner"><div class="page-section__content container"><div class="acds-featureSection row-flow undefined" style="anchor-name:--featureSection-enterprise-ready"><div class="acds-featureSection__children"><div class="page-content-banner acds-card"><div class="grid-container align-center justify-space-between" style="--columns:1;--columnWidth:1fr;--gap:3rem;--columnsManual:390px 660px"><div class="page-content-banner__title row-flow" style="--row-flow-margin:1rem"><h2 class="acds-body-xl acds-font-family-body acds-reset-text-transform">Enterprise-ready security and governance</h2><p class="acds-body-mono">Built for teams with strict compliance requirements</p></div><div class="page-content-banner__content flex-container"><span><ul class='list-reset flex-container'><li class='flex-container' style='gap:14px'><svg xmlns='http://www.w3.org/2000/svg' width='15' height='10' viewBox='0 0 15 10' fill='none'><path d='M14.5787 0.247623C14.8499 0.531711 14.8707 0.96339 14.6475 1.27007L14.5524 1.37869L5.75233 9.77869C5.48183 10.0369 5.07555 10.0692 4.77111 9.87551L4.64757 9.77868L0.247617 5.57868C-0.0719796 5.27361 -0.0837529 4.76721 0.22132 4.44762C0.492496 4.16353 0.922741 4.12266 1.23947 4.33135L1.35238 4.42132L5.1984 8.0928L13.4476 0.221315C13.7672 -0.0837554 14.2736 -0.0719767 14.5787 0.247623Z' fill='#19BA5A'/></svg> SOC 2 Type II</li><li class='flex-container' style='gap:14px'><svg xmlns='http://www.w3.org/2000/svg' width='15' height='10' viewBox='0 0 15 10' fill='none'><path d='M14.5787 0.247623C14.8499 0.531711 14.8707 0.96339 14.6475 1.27007L14.5524 1.37869L5.75233 9.77869C5.48183 10.0369 5.07555 10.0692 4.77111 9.87551L4.64757 9.77868L0.247617 5.57868C-0.0719796 5.27361 -0.0837529 4.76721 0.22132 4.44762C0.492496 4.16353 0.922741 4.12266 1.23947 4.33135L1.35238 4.42132L5.1984 8.0928L13.4476 0.221315C13.7672 -0.0837554 14.2736 -0.0719767 14.5787 0.247623Z' fill='#19BA5A'/></svg> HIPAA</li><li class='flex-container' style='gap:14px'><svg xmlns='http://www.w3.org/2000/svg' width='15' height='10' viewBox='0 0 15 10' fill='none'><path d='M14.5787 0.247623C14.8499 0.531711 14.8707 0.96339 14.6475 1.27007L14.5524 1.37869L5.75233 9.77869C5.48183 10.0369 5.07555 10.0692 4.77111 9.87551L4.64757 9.77868L0.247617 5.57868C-0.0719796 5.27361 -0.0837529 4.76721 0.22132 4.44762C0.492496 4.16353 0.922741 4.12266 1.23947 4.33135L1.35238 4.42132L5.1984 8.0928L13.4476 0.221315C13.7672 -0.0837554 14.2736 -0.0719767 14.5787 0.247623Z' fill='#19BA5A'/></svg> SSO/SCIM</li><li class='flex-container' style='gap:14px'><svg xmlns='http://www.w3.org/2000/svg' width='15' height='10' viewBox='0 0 15 10' fill='none'><path d='M14.5787 0.247623C14.8499 0.531711 14.8707 0.96339 14.6475 1.27007L14.5524 1.37869L5.75233 9.77869C5.48183 10.0369 5.07555 10.0692 4.77111 9.87551L4.64757 9.77868L0.247617 5.57868C-0.0719796 5.27361 -0.0837529 4.76721 0.22132 4.44762C0.492496 4.16353 0.922741 4.12266 1.23947 4.33135L1.35238 4.42132L5.1984 8.0928L13.4476 0.221315C13.7672 -0.0837554 14.2736 -0.0719767 14.5787 0.247623Z' fill='#19BA5A'/></svg> RBAC</li></ul></span><a href="/product/features#enterprise" class="acds-body-mono">View All â</a></div></div></div></div></div></div></section><section id="enterprise-ready" class="page-section page-stats"><div class="page-section__content container"><div class="acds-featureSection row-flow undefined" style="anchor-name:--featureSection-enterprise-ready"><div class="acds-featureSection__children"><div class="acds-stats-content page-stats-content"><div class="grid-container align-stretch justify-center" style="--columns:3;--columnWidth:1fr;--gap:24px"><div class="acds-card page-stat"><span class="page-stat__number">2.5x</span><span class="page-stat__description acds-body-mono">Concurrent tasks vs. managed alternatives</span></div><div class="acds-card page-stat"><span class="page-stat__number">1HR</span><span class="page-stat__description acds-body-mono">Support response SLA</span></div><div class="acds-card page-stat"><span class="page-stat__number">Day 0</span><span class="page-stat__description acds-body-mono">Airflow 3 access</span></div></div></div></div></div></div></section><section id="trusted" class="page-section page-featureStack"><div class="page-section__content container"><div class="acds-featureSection row-flow undefined" style="anchor-name:--featureSection-trusted"><div class="acds-featureSection__children"><div class="acds-feature-stack__top text-center row-flow"><h2 class="acds-h2 acds-text-white">Trusted by data and AI teams.</h2><p class="acds-body-lg acds-text-new-moon-10">From ETL to LLM pipelines, teams use Astro to orchestrate critical workflows.</p></div><div class="acds-feature-stack__items"><div class="acds-feature-stack__item acds-card"><div class="grid-container align-center justify-space-between" style="--columns:1;--columnWidth:1fr;--gap:3rem;--columnsManual:384px 533px"><div class="acds-feature-stack__item-text row-flow"><h3 class="acds-reset-text-transform acds-text-white sr-only">Everlane</h3><img src="/images/logos/everlane.png" alt="Everlane" loading="lazy" width="150" height="auto" style="filter:brightness(0) invert(1);margin-bottom:1rem"/><p class="acds-text-new-moon-10 acds-body-mono">Everlane runs 70+ critical Dags on Astro powering revenue metrics, inventory tracking, and churn prediction to support daily and strategic decisions across finance, marketing, logistics, and retail teams.</p><a class="acds-feature-stack__item-link acds-body-mono" href="/case-studies/everlane/">Learn More â</a></div><div class="acds-feature-stack__item-content"><a class="acds-testimonial-card acds-card" data-theme-color="gold" href="/case-studies/everlane/"><div class="acds-testimonial-card__stat"><div class="acds-testimonial-card__stat-number acds-text-white acds-h2">25%</div><div class="acds-testimonial-card__stat-text acds-text-new-moon-10 acds-body-sm">Cost Reduction</div></div><div class="acds-testimonial-card__content row-flow"><p class="acds-testimonial-card__company acds-tag">Everlane</p><p class="acds-testimonial-card__quote acds-body-sm acds-text-new-moon-10">"<!-- -->
437Astro gives us peace of mind. We donât worry about infrastructure. We focus on delivering data.<!-- -->"</p><div class="acds-testimonial-card__attribution acds-text-white flex-container"><p class="acds-testimonial-card__name acds-body-sm bold">Madhuri Gupta</p><span>·</span><p class="acds-testimonial-card__job-title acds-body-sm">Senior Manager of Data & Analytics</p></div></div></a></div></div></div><div class="acds-feature-stack__item acds-card"><div class="grid-container align-center justify-space-between" style="--columns:1;--columnWidth:1fr;--gap:3rem;--columnsManual:384px 533px"><div class="acds-feature-stack__item-text row-flow"><h3 class="acds-reset-text-transform acds-text-white sr-only">WeWork</h3><img src="/images/observe2/wework.png" alt="WeWork" loading="lazy" width="150" height="auto" style="filter:brightness(0) invert(1);margin-bottom:1rem"/><p class="acds-text-new-moon-10 acds-body-mono">WeWork's lean data team uses Astro to orchestrate data ingestion, transformation, and reporting pipelines globally, supporting decision-making across the business.</p><a class="acds-feature-stack__item-link acds-body-mono" href="/case-studies/wework/">Learn More â</a></div><div class="acds-feature-stack__item-content"><a class="acds-testimonial-card acds-card" data-theme-color="gold" href="/case-studies/wework/"><div class="acds-testimonial-card__stat"><div class="acds-testimonial-card__stat-number acds-text-white acds-h2">67%</div><div class="acds-testimonial-card__stat-text acds-text-new-moon-10 acds-body-sm">Infra Management Reduction</div></div><div class="acds-testimonial-card__content row-flow"><p class="acds-testimonial-card__company acds-tag">WeWork</p><p class="acds-testimonial-card__quote acds-body-sm acds-text-new-moon-10">"<!-- -->Astronomer makes doing more with less possible. It's a massively important part of our stack, since everything we buildâmodels, APIs, workflowsâare running on top of it.<!-- -->"</p><div class="acds-testimonial-card__attribution acds-text-white flex-container"><p class="acds-testimonial-card__name acds-body-sm bold">You Zhou</p><span>·</span><p class="acds-testimonial-card__job-title acds-body-sm">Director of Data Engineering</p></div></div></a></div></div></div><div class="acds-feature-stack__item acds-card"><div class="grid-container align-center justify-space-between" style="--columns:1;--columnWidth:1fr;--gap:3rem;--columnsManual:384px 533px"><div class="acds-feature-stack__item-text row-flow"><h3 class="acds-reset-text-transform acds-text-white sr-only">AAA Life Insurance</h3><img src="/images/customers/new/aaa.png" alt="AAA Life Insurance" loading="lazy" width="150" height="auto" style="filter:brightness(0) invert(1);margin-bottom:1rem"/><p class="acds-text-new-moon-10 acds-body-mono">AAA Life uses Astro to run dozens of production Dags including dbt transformations, reverse ETL to CRM systems, and S3 exports to deliver timely insights to 1.6 million policyholders.</p><a class="acds-feature-stack__item-link acds-body-mono" href="/case-studies/aaalife/">Learn More â</a></div><div class="acds-feature-stack__item-content"><a class="acds-testimonial-card acds-card" data-theme-color="gold" href="/case-studies/aaalife/"><div class="acds-testimonial-card__stat"><div class="acds-testimonial-card__stat-number acds-text-white acds-h2">80%</div><div class="acds-testimonial-card__stat-text acds-text-new-moon-10 acds-body-sm">Faster Trouble-shooting</div></div><div class="acds-testimonial-card__content row-flow"><p class="acds-testimonial-card__company acds-tag">AAA LIFE INSURANCE</p><p class="acds-testimonial-card__quote acds-body-sm acds-text-new-moon-10">"<!-- -->The biggest impact that I've seen personally is us being able to recover from failure much quicker, and that has had a significant impact on us being able to meet our SLAs.<!-- -->"</p><div class="acds-testimonial-card__attribution acds-text-white flex-container"><p class="acds-testimonial-card__name acds-body-sm bold">Josh Bickmeyer</p><span>·</span><p class="acds-testimonial-card__job-title acds-body-sm">Manager, Analytics Engineering</p></div></div></a></div></div></div></div></div></div></div></section></div><!--astro:end--></astro-island>
437<script>(()=>{var a=(s,i,o)=>{let r=async()=>{await(await s())()},t=typeof i.value=="object"?i.value:void 0,c={rootMargin:t==null?void 0:t.rootMargin},n=new IntersectionObserver(e=>{for(let l of e)if(l.isIntersecting){n.disconnect(),r();break}},c);for(let e of o.children)n.observe(e)};(self.Astro||(self.Astro={})).visible=a;window.dispatchEvent(new Event("astro:visible"));})();</script>
437<astro-island uid="ZGTdTO" prefix="r7" component-url="/_astro/NebulaCallToAction.CQWiaKP3.js?dpl=6abd1c0e8e511d0008ca4db3" component-export="default" renderer-url="/_astro/client.CgkIogCC.js?dpl=6abd1c0e8e511d0008ca4db3" props="{}" ssr client="visible" opts="{"name":"NebulaCallToAction","value":true}" await-children><div class="acds-nebula-call-to-action acds-nebula-call-to-action--has-bg" aria-hidden="true"><div class="acds-nebula-call-to-action__bg"><img src="/images/footer-cta-bg.svg" alt="" width="100%" height="100%"/></div></div><!--astro:end--></astro-island> </main> <footer class="acds-footer"><div class="acds-footer__inner"><div class="acds-footer__top"><nav class="acds-footer__nav" title="footer navigation"><div class="acds-footer__nav--list"><h3 class="acds-eyebrow" id="product">Product</h3><ul class="acds-footer__nav-list list-reset"><li><a class="acds-footer__nav-item" href="/product/">Platform Overview</a></li><li><a class="acds-footer__nav-item" href="/product/">Astro</a></li><li><a class="acds-footer__nav-item" href="/otto/">AI Features on Astro</a></li><li><a class="acds-footer__nav-item" href="/product/astro-private-cloud/">Astro Private Cloud</a></li><li><a class="acds-footer__nav-item" href="/security/">Security & Trust</a></li><li><a class="acds-footer__nav-item" href="/pricing/">Pricing</a></li><li><a class="acds-footer__nav-item" href="/product-tour/">Tour</a></li></ul></div><div class="acds-footer__nav--list"><h3 class="acds-eyebrow" id="tools-services">Tools & Services</h3><ul class="acds-footer__nav-list list-reset"><li><a class="acds-footer__nav-item" href="/cosmos/">Cosmos</a></li><li><a class="acds-footer__nav-item" href="https://airflow.apache.org/registry/">Registry</a></li><li><a class="acds-footer__nav-item" href="/docs/">Docs</a></li><li><a class="acds-footer__nav-item" href="/professional-services/">Professional Services</a></li><li><a class="acds-footer__nav-item" href="/product/updates/">Product Updates</a></li><li><a class="acds-footer__nav-item" href="/solutions/legacy-scheduler-migration/">Legacy Scheduler Migration</a></li><li><a class="acds-footer__nav-item" href="https://llms.astronomer.io/" aria-label="Learn More: llms.astronomer.io">Learn More</a></li></ul></div><div class="acds-footer__nav--list"><h3 class="acds-eyebrow" id="use-cases">Use Cases</h3><ul class="acds-footer__nav-list list-reset"><li><a class="acds-footer__nav-item" href="/solutions/ai-ml/">AI Ops</a></li><li><a class="acds-footer__nav-item" href="/solutions/data-observability/">Data Observability</a></li><li><a class="acds-footer__nav-item" href="/data-orchestration/">Data Orchestration</a></li><li><a class="acds-footer__nav-item" href="/solutions/etl-elt/">ETL/ELT</a></li><li><a class="acds-footer__nav-item" href="/solutions/mlops/">ML Ops</a></li><li><a class="acds-footer__nav-item" href="/solutions/analytics-and-reporting/">Operational Analytics</a></li><li><a class="acds-footer__nav-item" href="/solutions/">All Use Cases</a></li></ul></div><div class="acds-footer__nav--list"><h3 class="acds-eyebrow" id="industries">Industries</h3><ul class="acds-footer__nav-list list-reset"><li><a class="acds-footer__nav-item" href="/solutions/financial-services/">Financial Services</a></li><li><a class="acds-footer__nav-item" href="/solutions/gaming/">Gaming</a></li><li><a class="acds-footer__nav-item" href="/solutions/retail-ecommerce/">Retail</a></li><li><a class="acds-footer__nav-item" href="/solutions/manufacturing/">Manufacturing</a></li><li><a class="acds-footer__nav-item" href="/solutions/healthcare/">Healthcare</a></li><li><a class="acds-footer__nav-item" href="/solutions/">All Industries</a></li></ul></div><div class="acds-footer__nav--list"><h3 class="acds-eyebrow" id="resources">Resources</h3><ul class="acds-footer__nav-list list-reset"><li><a class="acds-footer__nav-item" href="https://academy.astronomer.io/">Academy</a></li><li><a class="acds-footer__nav-item" href="/resources/">eBooks & Guides</a></li><li><a class="acds-footer__nav-item" href="/blog/">Blog</a></li><li><a class="acds-footer__nav-item" href="/events/webinars/">Webinars</a></li><li><a class="acds-footer__nav-item" href="/events/">Events</a></li><li><a class="acds-footer__nav-item" href="/podcast/">The Data Flowcast Podcast</a></li><li><a class="acds-footer__nav-item" href="/events/beyond-the-dag-data-engineering-hackathon-2026/">Beyond the Dag Hackathon</a></li><li><a class="acds-footer__nav-item" href="/resources/">All Resources</a></li></ul></div><div class="acds-footer__nav--list"><h3 class="acds-eyebrow" id="airflow">Airflow</h3><ul class="acds-footer__nav-list list-reset"><li><a class="acds-footer__nav-item" href="/airflow/">What is Airflow</a></li><li><a class="acds-footer__nav-item" href="/astro-vs-apache-airflow-oss/">Airflow on Astro</a></li><li><a class="acds-footer__nav-item" href="/airflow/3/">Airflow 3.0</a></li><li><a class="acds-footer__nav-item" href="/blog/upgrading-airflow-2-to-airflow-3-a-checklist-for-2026/">Airflow Upgrades</a></li><li>
437<a class="acds-footer__nav-item" href="/airflow/use-cases/">Airflow Use Cases</a></li><li><a class="acds-footer__nav-item" href="/airflow-2-eol/">Airflow 2.x End of Life</a></li></ul></div><div class="acds-footer__nav--list"><h3 class="acds-eyebrow" id="company">Company</h3><ul class="acds-footer__nav-list list-reset"><li><a class="acds-footer__nav-item" href="/about-us/">About Us</a></li><li><a class="acds-footer__nav-item" href="/customers/">Customers</a></li><li><a class="acds-footer__nav-item" href="/press/">Newsroom</a></li><li><a class="acds-footer__nav-item" href="/careers/">Careers</a></li><li><a class="acds-footer__nav-item" href="/contact/">Contact</a></li></ul></div><div class="acds-footer__nav--list"><h3 class="acds-eyebrow" id="support">Support</h3><ul class="acds-footer__nav-list list-reset"><li><a class="acds-footer__nav-item" href="https://support.astronomer.io/hc/en-us">Knowledge Base</a></li><li><a class="acds-footer__nav-item" href="https://status.astronomer.io/">Status</a></li><li><a class="acds-footer__nav-item" href="https://support.astronomer.io/hc/en-us">Contact Support</a></li></ul></div></nav><div class="acds-footer__social"><a href="https://github.com/astronomer" target="_blank"><img src="/icons/github copy.svg" alt="GitHub" width="22" height="22" loading="lazy"/></a><a href="https://www.youtube.com/c/astronomer" target="_blank"><img src="/icons/youtube copy.svg" alt="YouTube" width="22" height="22" loading="lazy"/></a><a href="https://www.linkedin.com/company/astronomer/" target="_blank"><img src="/icons/linkedin copy.svg" alt="LinkedIn" width="22" height="22" loading="lazy"/></a><a href="https://x.com/astronomerio" target="_blank"><img src="/icons/x.svg" alt="x" width="22" height="22" loading="lazy"/></a></div></div><div class="acds-footer__bottom"><div class="acds-footer__bottom-links"><nav title="footer small links"><ul class="acds-footer__small-links list-reset flex-container"><li><a class="acds-footer__nav-item" href="/legal/">Legal</a></li><li><a class="acds-footer__nav-item" href="/privacy/">Privacy</a></li><li><a class="acds-footer__nav-item" href="/legal/terms-of-service/">Terms of Service</a></li><li><a href="#" class="acds-footer__nav-item termly-display-preferences">Consent Preferences</a></li></ul><br/><ul class="acds-footer__small-links list-reset flex-container"><li><a href="https://app.termly.io/notify/e016d430-8e37-4d86-b2f0-f0ee9589a67a" class="acds-footer__nav-item">Do Not Sell or Share My Personal information</a></li><li><a href="https://app.termly.io/notify/e016d430-8e37-4d86-b2f0-f0ee9589a67a" class="acds-footer__nav-item">Limit the Use Of My Sensitive Personal Information</a></li></ul></nav></div><p class="acds-footer__small-text"><a class="acds-apache-link" href="https://airflow.apache.org/">Apache Airflow<sup style="font-size:0.5em">®</sup></a>, Airflow, and the Airflow logo are trademarks of the<!-- --> <a class="acds-apache-link" href="https://apache.org/">Apache Software Foundation</a>. Copyright © Astronomer <!-- -->2026<!-- -->. All rights reserved.</p></div></div></footer> <pagefind-modal reset-on-close="true"> <pagefind-modal-header> <pagefind-input placeholder="Search"></pagefind-input> <div class="acds-search-filters"> <pagefind-filter-dropdown filter="category" label="Content Type" single-select="true" hide-clear="true" sort="count-desc"></pagefind-filter-dropdown> <pagefind-filter-dropdown filter="author" label="Author" hide-clear="true" sort="count-desc"></pagefind-filter-dropdown> </div> </pagefind-modal-header> <pagefind-modal-body> <pagefind-summary></pagefind-summary> <pagefind-grouped-results></pagefind-grouped-results> </pagefind-modal-body> <pagefind-modal-footer> <pagefind-keyboard-hints></pagefind-keyboard-hints> </pagefind-modal-footer> </pagefind-modal>
437<script type="module"> 438 // pagefind-grouped-results: custom results component that organises 439 // search results into sections by content type category. 440 // 441 // Runs after pagefind-component-ui.js (both type="module", ordered by 442 // document position) so window.PagefindComponents is always available 443 // by the time connectedCallback fires. 444 445 class PagefindGroupedResults extends HTMLElement { 446 #instance = null; 447 // Maximum results to fetch across all categories 448 static #MAX_RESULTS = 500; 449 // Categories that have publication dates â sorted newest-first within each group 450 static #DATE_CATEGORIES = new Set([ 451 "Blog Post", 452 "Case Study", 453 "Webinar", 454 "Podcast", 455 "eBook", 456 "Event", 457 "Press Release", 458 ]); 459 // Preferred display order for category headings 460 static #CATEGORY_ORDER = [ 461 "Blog Post", 462 "Case Study", 463 "Webinar", 464 "Podcast", 465 "eBook", 466 "Event", 467 "Course", 468 "Docs", 469 "Press Release", 470 "Page", 471 "Solution", 472 "Integration", 473 ]; 474 475 connectedCallback() { 476 const instanceName = this.getAttribute("instance") || "default"; 477 // Wait for the Component UI bundle to register its own elements, 478 // which guarantees window.PagefindComponents is ready. 479 customElements.whenDefined("pagefind-results").then(() => { 480 this.#instance = 481 window.PagefindComponents.getInstanceManager().getInstance( 482 instanceName, 483 ); 484 this.#instance.registerResults(this, { keyboardNavigation: true }); 485 this.#instance.on("loading", () => this.#showLoading()); 486 this.#instance.on("results", (sr) => this.#showResults(sr)); 487 this.#setupKeyNav(); 488 }); 489 } 490 491 // Called by the instance during ARIA reconciliation. 492 reconcileAria() {} 493 494 #showLoading() { 495 this.innerHTML = `<div class="acds-grouped-results__loading" aria-live="polite"> 496 <div class="pf-skeleton pf-skeleton-title"></div> 497 <div class="pf-skeleton pf-skeleton-excerpt"></div> 498 <div class="pf-skeleton pf-skeleton-title"></div> 499 <div class="pf-skeleton pf-skeleton-excerpt"></div> 500 </div>`; 501 } 502 503 async #showResults(searchResult) { 504 if (!searchResult.results?.length) { 505 // Filter-only mode: when the input is empty but filters are active, 506 // fall back to pagefind.search(null) which browses all indexed pages. 507 const filters = this.#instance.searchFilters ?? {}; 508 const hasFilters = Object.values(filters).some( 509 (v) => v?.length > 0, 510 ); 511 if (this.#getQuery() === "" && hasFilters) { 512 await this.#filterOnlySearch(filters); 513 return; 514 } 515 this.innerHTML = ""; 516 return; 517 } 518 await this.#renderGrouped(searchResult.results); 519 } 520 521 // Read the current value of the pagefind-input element. 522 #getQuery() { 523 const inputEl = document.querySelector("pagefind-input"); 524 if (!inputEl) return ""; 525 // Custom element may expose .value directly; fall back to shadow DOM. 526 if (typeof inputEl.value === "string") return inputEl.value.trim(); 527 const inner = inputEl.shadowRoot?.querySelector("input"); 528 return (inner?.value ?? "").trim(); 529 } 530 531 // Use the raw Pagefind JS API to search with no text term but active 532 // filters. The component UI doesn't support empty-query searches, so 533 // we call pagefind.search(null, { filters }) directly. 534 async #filterOnlySearch(filters) { 535 this.#showLoading(); 536 try { 537 const pf = await import("/pagefind/pagefind.js"); 538 const result = await pf.search(null, { filters }); 539 if (!result.results?.length) { 540 this.innerHTML = ""; 541 return; 542 } 543 await this.#renderGrouped(result.results); 544 } catch { 545 this.innerHTML = ""; 546 } 547 } 548 549 async #renderGrouped(results) { 550 // Fetch result data for the top N results only 551 const rawResults = results.slice( 552 0, 553 PagefindGroupedResults.#MAX_RESULTS, 554 ); 555 const allData = await Promise.all(rawResults.map((r) => r.data())); 556 557 // Group by category filter value 558 const groups = new Map(); 559 for (const data of allData) { 560 const category = data.filters?.category?.[0] ?? "Page"; 561 if (!groups.has(category)) groups.set(category, []); 562 groups.get(category).push(data); 563 } 564
565 // Within date-based categories, sort newest first 566 for (const [category, items] of groups) { 567 if (PagefindGroupedResults.#DATE_CATEGORIES.has(category)) { 568 items.sort((a, b) => { 569 const da = a.meta?.date ?? ""; 570 const db = b.meta?.date ?? ""; 571 return db.localeCompare(da); 572 }); 573 } 574 } 575 576 // Sort category sections by the preferred order, then alphabetically 577 const ordered = PagefindGroupedResults.#CATEGORY_ORDER.filter((c) => 578 groups.has(c), 579 ); 580 const remaining = [...groups.keys()] 581 .filter((c) => !PagefindGroupedResults.#CATEGORY_ORDER.includes(c)) 582 .sort(); 583 584 const html = [...ordered, ...remaining] 585 .map((category) => { 586 const groupId = `pf-group-${category 587 .toLowerCase() 588 .replace(/\s+/g, "-") 589 .replace(/[^a-z0-9-]/g, "")}`; 590 const items = groups 591 .get(category) 592 .map( 593 (data) => ` 594 <li class="acds-grouped-results__item"> 595 <a class="acds-grouped-results__link" href="${this.#safe(this.#resolveUrl(data.url))}"> 596 <span class="acds-grouped-results__title">${this.#safe(data.meta?.title ?? "Untitled")}</span> 597 ${data.excerpt ? `<p class="acds-grouped-results__excerpt">${data.excerpt.replace("Blog|", "")}</p>` : ""} 598 </a> 599 </li>`, 600 ) 601 .join(""); 602 return ` 603 <section class="acds-grouped-results__section" aria-labelledby="${groupId}"> 604 <h3 class="acds-grouped-results__heading" id="${groupId}">${this.#safe(category)}</h3> 605 <ul class="acds-grouped-results__list" role="list">${items}</ul> 606 </section>`; 607 }) 608 .join(""); 609 610 this.innerHTML = html; 611 this.#instance.reconcileAria(); 612 } 613 614 // Keyboard navigation: mirror pagefind-results behaviour 615 #setupKeyNav() { 616 this.addEventListener("keydown", (e) => { 617 const focused = document.activeElement; 618 if (!focused || !this.contains(focused) || focused.tagName !== "A") 619 return; 620 621 const links = [ 622 ...this.querySelectorAll(".acds-grouped-results__link"), 623 ]; 624 const idx = links.indexOf(focused); 625 626 if (e.key === "ArrowDown") { 627 if (idx < links.length - 1) links[idx + 1].focus(); 628 e.preventDefault(); 629 } else if (e.key === "ArrowUp") { 630 if (idx > 0) { 631 links[idx - 1].focus(); 632 } else { 633 this.#instance.focusPreviousInput(this); 634 } 635 e.preventDefault(); 636 } else if (e.key === "Backspace") { 637 this.#instance.focusInputAndDelete(this); 638 e.preventDefault(); 639 } else if ( 640 e.key.length === 1 && 641 !e.ctrlKey && 642 !e.metaKey && 643 !e.altKey 644 ) { 645 this.#instance.focusInputAndType(this, e.key); 646 e.preventDefault(); 647 } 648 }); 649 } 650 651 // Escape text for safe insertion as HTML text / attribute value. 652 // NOTE: data.excerpt is intentionally NOT passed here â it contains 653 // Pagefind-generated <mark> tags that must be preserved. 654 #safe(str) { 655 if (!str) return ""; 656 return String(str) 657 .replace(/&/g, "&") 658 .replace(/</g, "<") 659 .replace(/>/g, ">") 660 .replace(/"/g, """); 661 } 662 663 // Convert a /.docs-content/docs__astro__create-deployment.html path 664 // back to the real /docs/astro/create-deployment URL. 665 // Convert a /.academy-content/path__airflow-101.html path 666 // back to the full https://academy.astronomer.io/path/airflow-101 URL. 667 #resolveUrl(url) { 668 if (!url) return url; 669 const docsMatch = url.match(/^\/\.docs-content\/(.+)\.html$/); 670 if (docsMatch) { 671 return "/" + docsMatch[1].replace(/__/g, "/"); 672 } 673 const academyMatch = url.match(/^\/\.academy-content\/(.+)\.html$/); 674 if (academyMatch) { 675 return ( 676 "https://academy.astronomer.io/" + 677 academyMatch[1].replace(/__/g, "/") 678 ); 679 } 680 return url; 681 } 682 } 683 684 customElements.define("pagefind-grouped-results", PagefindGroupedResults); 685 686 // ======================================================================= 687 // Dropdown enhancements 688 // Runs once after pagefind-component-ui.js has registered its elements. 689 // 690 // 1. syncDropdownLabels â replaces the generic label ("Content Type") with 691 // the active selection ("Blog Post") and marks the trigger as active. 692 // 2. setupClearButton â injects a sticky "Clear" button at the bottom of 693 // each dropdown menu, hidden until a value is selected. 694 // ======================================================================= 695 696 customElements.whenDefined("pagefind-results").then(() => { 697 const instance = 698 window.PagefindComponents.getInstanceManager().getInstance("default"); 699 700 // Re-sync labels whenever filters change 701 instance.on("filters", () => syncDropdownLabels(instance)); 702 703 // Inject clear buttons into each dropdown (may be lazy-rendered) 704 document 705 .querySelectorAll("pagefind-filter-dropdown") 706 .forEach((dd) => setupClearButton(dd, instance)); 707 708 // Initial pass in case filters are already set 709 syncDropdownLabels(instance); 710 }); 711 712 function syncDropdownLabels(instance) { 713 const filters = instance.searchFilters ?? {}; 714 document.querySelectorAll("pagefind-filter-dropdown").forEach((dd) => {
715 const key = dd.getAttribute("filter"); 716 const defaultLabel = dd.getAttribute("label") || key; 717 const selected = filters[key]; 718 719 // Replace label text with the active value (or restore the default) 720 const labelEl = dd.querySelector(".pf-dropdown-trigger-label"); 721 if (labelEl) { 722 labelEl.textContent = selected?.length ? selected[0] : defaultLabel; 723 } 724 725 // Drive the active CSS state via a data attribute 726 if (selected?.length) { 727 dd.setAttribute("data-pf-active", ""); 728 } else { 729 dd.removeAttribute("data-pf-active"); 730 } 731 732 // Show/hide the inline Clear button to match filter state 733 const clearBtn = dd.querySelector(".acds-dropdown-clear-inline"); 734 if (clearBtn) clearBtn.hidden = !selected?.length; 735 }); 736 } 737 738 function setupClearButton(dd, instance) { 739 const key = dd.getAttribute("filter"); 740 741 const inject = () => { 742 if (dd.querySelector(".acds-dropdown-clear-inline")) return; 743 const menu = dd.querySelector(".pf-dropdown-menu"); 744 if (!menu) return; 745 746 const btn = document.createElement("button"); 747 btn.className = "acds-dropdown-clear-inline"; 748 btn.type = "button"; 749 btn.textContent = "Clear"; 750 btn.hidden = true; // shown via syncDropdownLabels when a value is selected 751 752 btn.addEventListener("click", (e) => { 753 e.stopPropagation(); 754 instance.triggerFilter(key, []); 755 // Close the dropdown after clearing 756 requestAnimationFrame(() => { 757 const trigger = dd.querySelector(".pf-dropdown-trigger"); 758 if (trigger && trigger.classList.contains("open")) 759 trigger.click(); 760 }); 761 }); 762 763 menu.appendChild(btn); 764 obs.disconnect(); 765 }; 766 767 // Watch for the menu element in case it is rendered lazily 768 const obs = new MutationObserver(inject); 769 obs.observe(dd, { childList: true, subtree: true }); 770 inject(); // also try immediately 771 } 772 773 // ======================================================================= 774 // Search Analytics 775 // Fires to PostHog (window.posthog.capture) and GTM (window.dataLayer) 776 // for every meaningful search interaction. Uses the same pattern as form 777 // tracking elsewhere on the site. 778 // 779 // Events fired: 780 // Search Opened â modal becomes visible 781 // Search Queried â user pauses typing (â¥2 chars, debounced 600ms) 782 // Search Filter Applied â Content Type or Author filter changes 783 // Search Result Clicked â user clicks a result link 784 // Search Closed â modal is dismissed 785 // ======================================================================= 786 customElements.whenDefined("pagefind-results").then(() => { 787 const instance = 788 window.PagefindComponents.getInstanceManager().getInstance("default"); 789 const modal = document.querySelector("pagefind-modal"); 790 if (!modal) return; 791 792 // ââ Helper: fires to PostHog and GTM / GA4 âââââââââââââââââââââ 793 function trackSearch(event, props) { 794 const payload = { component: "site_search", ...props }; 795 if (window.posthog?.capture) { 796 window.posthog.capture(event, payload); 797 } 798 if (window.dataLayer) { 799 window.dataLayer.push({ 800 event: "site_search", 801 search_event: event, 802 search_data: payload, 803 }); 804 } 805 } 806 807 // ââ Shared state âââââââââââââââââââââââââââââââââââââââââââââââââââââ 808 let lastQuery = ""; 809 let lastResultCount = 0; 810 let lastFilters = {}; 811 let clickedResultSinceOpen = false; 812 813 // ââ Modal open / close ââââââââââââââââââââââââââââââââââââââââââââââââ 814 // pagefind-modal renders a native <dialog> element which carries the 815 // `open` attribute and fires the native `close` event â both are reliable 816 // regardless of how the modal is triggered (nav button, keyboard shortcut). 817 function attachModalTracking(dialog) { 818 let isOpen = dialog.hasAttribute("open"); 819 820 new MutationObserver(() => { 821 const nowOpen = dialog.hasAttribute("open"); 822 if (nowOpen === isOpen) return; 823 isOpen = nowOpen; 824 if (isOpen) { 825 clickedResultSinceOpen = false;
826 trackSearch("Search Opened"); 827 } 828 }).observe(dialog, { attributes: true, attributeFilter: ["open"] }); 829 830 dialog.addEventListener("close", () => { 831 trackSearch("Search Closed", { 832 ...(lastQuery && { query: lastQuery }), 833 result_count: lastResultCount, 834 clicked_result: clickedResultSinceOpen, 835 }); 836 lastQuery = ""; 837 lastResultCount = 0; 838 clickedResultSinceOpen = false; 839 }); 840 } 841 842 // The <dialog> may be rendered lazily inside the custom element 843 const existingDialog = 844 modal.querySelector("dialog") ?? 845 modal.shadowRoot?.querySelector("dialog"); 846 if (existingDialog) { 847 attachModalTracking(existingDialog); 848 } else { 849 new MutationObserver((_, obs) => { 850 const d = 851 modal.querySelector("dialog") ?? 852 modal.shadowRoot?.querySelector("dialog"); 853 if (d) { 854 obs.disconnect(); 855 attachModalTracking(d); 856 } 857 }).observe(modal, { childList: true, subtree: true }); 858 } 859 860 // ââ Search queries: debounced 600ms, min 2 chars âââââââââââââââââââââ 861 let queryTimer = null; 862 modal 863 .querySelector("pagefind-input") 864 ?.addEventListener("input", (e) => { 865 // input bubbles from the shadow-DOM <input> â composedPath gives us 866 // the actual element so we can read its current value. 867 const query = (e.composedPath?.()?.[0]?.value ?? "").trim(); 868 lastQuery = query; 869 clearTimeout(queryTimer); 870 if (query.length < 2) return; 871 queryTimer = setTimeout(() => { 872 trackSearch("Search Queried", { 873 query, 874 result_count: lastResultCount, 875 ...(Object.keys(instance.searchFilters ?? {}).length && { 876 filters: instance.searchFilters, 877 }), 878 }); 879 }, 600); 880 }); 881 882 // Keep lastResultCount current for Search Closed 883 instance.on("results", (sr) => { 884 lastResultCount = sr.results?.length ?? 0; 885 }); 886 887 // ââ Filter applied ââââââââââââââââââââââââââââââââââââââââââââââââââââ 888 instance.on("filters", () => { 889 const newFilters = instance.searchFilters ?? {}; 890 const allKeys = new Set([ 891 ...Object.keys(lastFilters), 892 ...Object.keys(newFilters), 893 ]); 894 for (const key of allKeys) { 895 const prev = (lastFilters[key] ?? []).join(","); 896 const curr = (newFilters[key] ?? []).join(","); 897 if (prev !== curr && curr) { 898 trackSearch("Search Filter Applied", { 899 filter_type: key, 900 filter_value: newFilters[key]?.[0], 901 ...(lastQuery && { query: lastQuery }), 902 }); 903 } 904 } 905 lastFilters = { ...newFilters }; 906 }); 907 908 // ââ Result clicks âââââââââââââââââââââââââââââââââââââââââââââââââââââ 909 // Delegated listener on the modal so it works even after re-renders 910 modal.addEventListener("click", (e) => { 911 const link = e.target.closest?.(".acds-grouped-results__link"); 912 if (!link) return; 913 914 const section = link.closest(".acds-grouped-results__section"); 915 const category = 916 section 917 ?.querySelector(".acds-grouped-results__heading") 918 ?.textContent?.trim() ?? ""; 919 const title = 920 link 921 .querySelector(".acds-grouped-results__title") 922 ?.textContent?.trim() ?? ""; 923 const href = link.getAttribute("href") ?? ""; 924 const allLinks = [ 925 ...modal.querySelectorAll(".acds-grouped-results__link"), 926 ]; 927 const position = allLinks.indexOf(link) + 1; 928 929 clickedResultSinceOpen = true; 930 trackSearch("Search Result Clicked", { 931 query: lastQuery || undefined, 932 result_url: href, 933 result_title: title, 934 result_category: category, 935 result_position: position, 936 ...(Object.keys(instance.searchFilters ?? {}).length && { 937 filters: instance.searchFilters, 938 }), 939 }); 940 }); 941 }); 942 </script>
942<script> 943 window.addEventListener("DOMContentLoaded", () => { 944 if ( 945 !document.querySelector(".headerNav") && 946 document.querySelector(".menuToggle") 947 ) { 948 document.querySelector(".menuToggle").remove(); 949 } 950 setTimeout(() => { 951 document.querySelector("body").classList.remove("preload"); 952 }, 100); 953 954 if (window.location.search.includes("utm")) { 955 const params = new URLSearchParams(window.location.search); 956 [...document.querySelectorAll("a[href]")].forEach((e) => { 957 const url = new URL(e.href); 958 for (let [k, v] of params.entries()) { 959 url.searchParams.set(k, v); 960 } 961 e.href = url.toString(); 962 }); 963 } 964 }); 965 </script>
965</body> </html>
965<script> 966 // Make sure Wistia's marketo object is always deleted so the webianr recap form always shows 967 document.addEventListener("DOMContentLoaded", function () { 968 localStorage.removeItem("wistia"); 969 }); 970 971 // Globally Available IntersectionObserve to lazy load videos with class .lazy 972 // https://web.dev/articles/lazy-loading-video 973 document.addEventListener("DOMContentLoaded", function () { 974 let lazyVideos = [].slice.call(document.querySelectorAll("video.lazy")); 975 976 if ("IntersectionObserver" in window) { 977 let lazyVideoObserver = new IntersectionObserver(function ( 978 entries, 979 observer, 980 ) { 981 entries.forEach(function (video) { 982 if (video.isIntersecting) { 983 for (let source in video.target.children) { 984 let videoSource = video.target.children[source]; 985 if ( 986 typeof videoSource.tagName === "string" && 987 videoSource.tagName === "SOURCE" 988 ) { 989 videoSource.src = videoSource.dataset.src; 990 } 991 } 992 993 video.target.load(); 994 video.target.classList.remove("lazy"); 995 lazyVideoObserver.unobserve(video.target); 996 } 997 }); 998 }); 999 1000 lazyVideos.forEach(function (lazyVideo) { 1001 lazyVideoObserver.observe(lazyVideo); 1002 }); 1003 } 1004 }); 1005</script>
1005
1005<script> 1006 document.addEventListener("DOMContentLoaded", function () { 1007 const modelContext = 1008 document.modelContext || 1009 (typeof navigator !== "undefined" ? navigator.modelContext : undefined); 1010 1011 if (!modelContext) { 1012 return; 1013 } 1014 1015 function toSameOriginUrl(inputPath, fallbackPath) { 1016 const rawPath = 1017 typeof inputPath === "string" && inputPath.trim() 1018 ? inputPath.trim() 1019 : fallbackPath; 1020 const pathWithLeadingSlash = rawPath.startsWith("/") 1021 ? rawPath 1022 : `/${rawPath}`; 1023 const normalized = new URL(pathWithLeadingSlash, window.location.origin); 1024 1025 if (normalized.origin !== window.location.origin) { 1026 throw new Error("Cross-origin navigation is not allowed"); 1027 } 1028 1029 return normalized; 1030 } 1031 1032 const tools = [ 1033 { 1034 name: "open_docs", 1035 description: "Navigate to Astronomer documentation.", 1036 inputSchema: { 1037 type: "object", 1038 properties: { 1039 path: { 1040 type: "string", 1041 description: "Optional docs path, for example /docs/astro/.", 1042 }, 1043 }, 1044 }, 1045 execute: async function (input) { 1046 try { 1047 const requestedPath = 1048 input && typeof input.path === "string" ? input.path.trim() : ""; 1049 const docsPath = requestedPath 1050 ? requestedPath.startsWith("/docs") 1051 ? requestedPath 1052 : `/docs/${requestedPath.replace(/^\/+/, "")}` 1053 : "/docs"; 1054 const url = toSameOriginUrl(docsPath, "/docs"); 1055 const href = `${url.pathname}${url.search}${url.hash}`; 1056 window.location.assign(href); 1057 1058 return { 1059 ok: true, 1060 url: url.toString(), 1061 }; 1062 } catch { 1063 return { 1064 ok: false, 1065 error: "Invalid path", 1066 }; 1067 } 1068 }, 1069 }, 1070 { 1071 name: "book_demo", 1072 description: "Navigate to the Astronomer demo booking page.", 1073 inputSchema: { 1074 type: "object", 1075 properties: {}, 1076 }, 1077 execute: async function () { 1078 const href = "/book-a-demo"; 1079 window.location.assign(href); 1080 1081 return { 1082 ok: true, 1083 url: new URL(href, window.location.origin).toString(), 1084 }; 1085 }, 1086 }, 1087 { 1088 name: "open_site_path", 1089 description: 1090 "Navigate to a path on www.astronomer.io while staying on the same origin.", 1091 inputSchema: { 1092 type: "object", 1093 properties: { 1094 path: { 1095 type: "string", 1096 description: "Absolute path on the site, for example /contact.", 1097 }, 1098 }, 1099 required: ["path"], 1100 }, 1101 execute: async function (input) { 1102 try { 1103 const rawPath = 1104 input && typeof input.path === "string" ? input.path.trim() : ""; 1105 const url = toSameOriginUrl(rawPath, "/"); 1106 const href = `${url.pathname}${url.search}${url.hash}`; 1107 window.location.assign(href); 1108 1109 return { 1110 ok: true, 1111 url: url.toString(), 1112 }; 1113 } catch { 1114 return { 1115 ok: false, 1116 error: "Invalid path", 1117 }; 1118 } 1119 }, 1120 }, 1121 { 1122 name: "get_discovery_endpoints", 1123 description: 1124 "Return machine-readable discovery endpoints published by www.astronomer.io.", 1125 inputSchema: { 1126 type: "object", 1127 properties: {}, 1128 }, 1129 annotations: { 1130 readOnlyHint: true, 1131 }, 1132 execute: async function () { 1133 const origin = window.location.origin; 1134 return { 1135 apiCatalog: `${origin}/.well-known/api-catalog`, 1136 openApi: `${origin}/.well-known/openapi.json`, 1137 agentSkills: `${origin}/.well-known/agent-skills/index.json`, 1138 mcpServerCard: `${origin}/.well-known/mcp/server-card.json`, 1139 }; 1140 }, 1141 }, 1142 ]; 1143 1144 if (typeof modelContext.registerTool === "function") { 1145 const abortController = new AbortController(); 1146 1147 Promise.allSettled( 1148 tools.map((tool) => 1149 modelContext.registerTool(tool, { 1150 signal: abortController.signal, 1151 }), 1152 ), 1153 ).catch(function () { 1154 // Ignore registration failures on browsers that expose partial previews. 1155 }); 1156 1157 window.addEventListener( 1158 "pagehide", 1159 function () { 1160 abortController.abort("navigation"); 1161 }, 1162 { once: true }, 1163 ); 1164 } 1165 1166 const navigatorModelContext = 1167 typeof navigator !== "undefined" ? navigator.modelContext : undefined; 1168 1169 if ( 1170 navigatorModelContext && 1171 typeof navigatorModelContext.provideContext === "function" 1172 ) {
1173 try { 1174 navigatorModelContext.provideContext({ 1175 tools: tools.map((tool) => ({ 1176 name: tool.name, 1177 description: tool.description, 1178 inputSchema: tool.inputSchema, 1179 })), 1180 }); 1181 } catch { 1182 // Ignore provideContext errors for experimental implementations. 1183 } 1184 } 1185 }); 1186</script>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.