{
    "version": "https://jsonfeed.org/version/1",
    "title": "Orbital Changelog",
    "home_page_url": "https://orbitalhq.com/changelog",
    "feed_url": "https://orbitalhq.com/feeds/changelog.json",
    "description": "What changed in each Orbital release.",
    "icon": "https://orbitalhq.com/brand/orbital-mark.png",
    "author": {
        "name": "Orbital",
        "url": "https://orbitalhq.com"
    },
    "items": [
        {
            "id": "https://orbitalhq.com/changelog/2026-08-24-release-announcement-0.39.0",
            "content_html": "<a href=\"https://hub.docker.com/r/orbitalhq/orbital/tags?name=0.39.1-M1\" target=\"_blank\" rel=\"noreferrer\">Available since <!-- -->0.39.1-M1</a>\n<p>0.39.1-M1 is the first milestone of 0.39 — the largest change Orbital has had. It has been in development since\nApril, and it changes the shape of the product in a few significant ways.</p>\n<p>The headline is <strong>workspaces</strong>: a single Orbital instance can now run many independent schemas, each with its\nown projects, its own compiled schema, its own connections and its own query context. Everything downstream of\nthat — auth, secrets, search, history, caching, the language server — was reworked to be workspace-aware.</p>\n<p>Alongside that: a <strong>secrets manager</strong> with five backends, a <strong>data validation framework</strong>, <strong>Copilot</strong>\n(chat-driven querying and schema building), an <strong>MCP server</strong> so external agents can query your data mesh, and\na <strong>new code editor</strong> that edits whole projects rather than single queries.</p>\n<p>Two features we announced over the summer also ship here for the first time: <strong>collection options</strong>\n(<code>limit</code>, <code>offset</code>, <code>orderBy</code>, <code>uniqueBy</code> on any query, pushed down to the source where it can take them), and\n<strong><code>@DeleteOperation</code> / <code>@SqlQuery</code></strong> for the database connectors.</p>\n<p>As a milestone, 0.39.1-M1 is for early adopters and teams who want to test ahead of the full 0.39 release.\nExpect rough edges, and tell us about them.</p>\n<aside role=\"note\"><p><span>Read the breaking changes first</span></p><p>This milestone requires a license, renames a feature toggle, migrates the query history schema, and removes\nthe legacy Jet pipeline server. Start with <a href=\"#breaking-changes\">Breaking changes</a> before you upgrade.</p></aside>\n<div><span></span><span></span></div>\n<h2 id=\"breaking-changes\"><a href=\"#breaking-changes\">​<span></span></a>Breaking changes</h2>\n<h3 id=\"orbital-now-requires-a-license\"><a href=\"#orbital-now-requires-a-license\">​<span></span></a>Orbital now requires a license<span><span>Breaking change</span></span></h3>\n<p>Orbital checks for a <code>license.json</code> on startup. When you first sign in, Orbital contacts the license server at\n<code>https://account.orbitalhq.app</code>, downloads a <strong>free</strong> license and installs it. In most environments this needs\nno setup.</p>\n<p>If no license is found, Orbital issues a short-lived fallback license so the server still starts — but that\nfallback only lasts <strong>30 minutes</strong>, after which the UI shows a blocking overlay.</p>\n<p>Orbital searches these paths in order, and the first valid license wins:</p>\n<ol role=\"list\">\n<li><code>--vyne.license.path</code> (if set)</li>\n<li><code>${vyne.app.data.path}/license.json</code> — by default <code>./orbital_data/license.json</code></li>\n<li><code>~/.orbital/license.json</code></li>\n<li><code>/opt/var/orbital/license/license.json</code></li>\n</ol>\n<p>For air-gapped deployments that can’t reach the license server, Orbital supports an air-gapped mode that\ndisables usage reporting. <a href=\"mailto:hello@orbitalhq.com\">Get in touch</a> if you need it.</p>\n<p>See <a href=\"https://orbitalhq.com/docs/deploying/configuring-your-license\">configuring your license</a> for the full detail.</p>\n<p>One improvement worth calling out: an expired license no longer terminates the JVM. The old <code>LicenseMonitor</code>\ncalled <code>exitProcess(0)</code> on expiry. That’s gone — expiry now surfaces in the UI and is governed by the license\npolicy instead of killing the process.</p>\n<h3 id=\"the-workspaces-enabled-toggle-is-replaced-by-workspace-mode\"><a href=\"#the-workspaces-enabled-toggle-is-replaced-by-workspace-mode\">​<span></span></a>The workspaces-enabled toggle is replaced by workspace-mode<span><span>Breaking change</span></span></h3>\n<p>The boolean toggle is gone, replaced by a three-state enum:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>vyne</span><span>:</span></span><span><span>  </span><span>toggles</span><span>:</span></span><span><span>    </span><span>workspace-mode</span><span>:</span><span> None    </span><span># None | Workspace | OrgAndWorkspace</span></span></code></pre></div></div>\n<div><table><thead><tr><th>Mode</th><th>Behaviour</th></tr></thead><tbody><tr><td><code>None</code> (default)</td><td>Single workspace. No workspace prefix in UI routes, no selector. Behaves as Orbital always has.</td></tr><tr><td><code>Workspace</code></td><td>Workspace routing. UI routes are prefixed <code>/{workspaceId}/...</code></td></tr><tr><td><code>OrgAndWorkspace</code></td><td>Org and workspace routing: <code>/{orgId}/{workspaceId}/...</code></td></tr></tbody></table></div>\n<p>If you were setting <code>workspacesEnabled: true</code>, that key is now ignored and you will silently fall back to\n<code>None</code>. Set <code>workspace-mode</code> instead.</p>\n<aside role=\"note\"><p><span>Use the kebab-case spelling</span></p><p>Some of the code reads this property through a <code>@Value</code> placeholder, which does not get Spring’s relaxed\nbinding. Always spell it <code>vyne.toggles.workspace-mode</code>, not <code>workspaceMode</code>.</p></aside>\n<h3 id=\"query-history-tables-gain-non-null-orgid-and-workspace_slug-columns\"><a href=\"#query-history-tables-gain-non-null-orgid-and-workspace_slug-columns\">​<span></span></a>Query history tables gain non-null orgId and workspace_slug columns<span><span>Breaking change</span></span></h3>\n<p>A Flyway migration adds a <code>workspace_slug</code> column across eight history tables, adds <code>orgId</code> to\n<code>query_error_event</code> and <code>trace_event</code>, backfills both with <code>default</code>, and enforces <code>NOT NULL</code>. Indexes on\n<code>(orgId, workspace_slug)</code> are created alongside.</p>\n<p>The migration runs automatically. On instances with a large query history, the <code>SET NOT NULL</code> statements take\na table lock, so budget for some downtime on the first boot after upgrading.</p>\n<p>The same migration drops the Postgres row-level-security policies on those tables. RLS was keyed on\n<code>current_user</code>, which never worked with a shared connection pool. Org and workspace isolation is now enforced\nin the application layer via org-membership checks and explicit <code>orgId</code> / <code>workspace_slug</code> filters on every\nquery.</p>\n<p>Rows written before this milestone cannot be attributed to a workspace — they were never stamped — so they stay\nas <code>default</code>/<code>default</code>.</p>\n<h3 id=\"the-legacy-jet-pipeline-server-has-been-removed\"><a href=\"#the-legacy-jet-pipeline-server-has-been-removed\">​<span></span></a>The legacy Jet pipeline server has been removed<span><span>Breaking change</span><span>Removed</span></span></h3>\n<p><code>pipelines/pipeline-jet</code> is gone from the build, along with the pipeline transports that only it used (S3,\nSQS, Cask, file-watcher, HTTP listener, JDBC, Kafka, Redshift, logging sink, polling query input) and the\ntransform stage.</p>\n<p>This is the standalone pipeline runner, which has been unmaintained and undocumented for a long time.\n<strong>Streaming queries are unaffected</strong> — <code>stream { }</code> queries run through <code>pipelines/stream-engine</code>, which stays.</p>\n<h3 id=\"h2-is-no-longer-a-shipped-jdbc-driver\"><a href=\"#h2-is-no-longer-a-shipped-jdbc-driver\">​<span></span></a>H2 is no longer a shipped JDBC driver<span><span>Breaking change</span><span>Removed</span></span></h3>\n<p><code>H2DatabaseSupport</code> has moved to test sources. The drivers shipped to end users are now Postgres, MSSQL,\nOracle, Redshift, Snowflake and Databricks. If you had an H2 connection defined in <code>connections.conf</code>, it will\nfail to resolve.</p>\n<h3 id=\"spring-boot-4-and-jackson-3\"><a href=\"#spring-boot-4-and-jackson-3\">​<span></span></a>Spring Boot 4 and Jackson 3<span><span>Breaking change</span></span></h3>\n<p>Orbital is built on Spring Boot 4.0 and Jackson 3. If you write custom Orbital extensions — custom taxi\nfunctions, custom connectors, anything compiled against Orbital’s jars — you will need to migrate. The main\nthings that bite:</p>\n<ul role=\"list\">\n<li>Jackson 2 → 3: the mapper and builder are immutable, and several APIs were renamed. The annotation package\nis still <code>com.fasterxml</code>.</li>\n<li>Spring 7 / Security 7 JSpecify nullability, and <code>: Any</code> generic bounds in Kotlin.</li>\n<li>The Spring Boot 4 modular auto-config split (<code>spring-boot-health</code>, <code>-jdbc</code>, <code>-hibernate</code>, <code>-mongodb</code>, and the\nmatching test slices).</li>\n<li>Testcontainers 1.x → 2.x, if your tests use it.</li>\n</ul>\n<h3 id=\"numeric-comparison-is-now-type--and-scale-insensitive\"><a href=\"#numeric-comparison-is-now-type--and-scale-insensitive\">​<span></span></a>Numeric comparison is now type- and scale-insensitive<span><span>Breaking change</span><span>Fixed</span></span></h3>\n<p>Comparisons between numbers of different JVM types now coerce both sides to <code>BigDecimal</code> and compare with\n<code>compareTo</code>. Two consequences:</p>\n<ul role=\"list\">\n<li>Cross-type numeric comparisons work. Previously only a narrow set of pairings (such as <code>Int</code> vs\n<code>BigDecimal</code>) compared correctly; others silently failed.</li>\n<li><code>Equal</code> and <code>NotEqual</code> are now scale-insensitive. <code>2.50 == 2.5</code> is now <code>true</code> where it was previously\n<code>false</code>, because <code>BigDecimal.equals</code> treats different scales as different values.</li>\n</ul>\n<p>Taxi’s <code>Long</code> is backed by <code>BigInteger</code>, which previously had no branch in the arithmetic calculator at all\nand errored with <em>“Unsupported number type”</em>. <code>Long</code> arithmetic now works, and division on <code>Long</code> truncates —\nthe same semantics as <code>Int</code>.</p>\n<h3 id=\"the-right-function-now-reads-from-the-right\"><a href=\"#the-right-function-now-reads-from-the-right\">​<span></span></a>The right() function now reads from the right<span><span>Breaking change</span><span>Fixed</span></span></h3>\n<p><code>right(input, n)</code> was returning characters counted from the <em>left</em> of the string. It now returns the last <code>n</code>\ncharacters, as the name and documentation always said. If you worked around the old behaviour, remove the\nworkaround.</p>\n<h3 id=\"operation-parameters-are-no-longer-matched-to-raw-primitives-by-type\"><a href=\"#operation-parameters-are-no-longer-matched-to-raw-primitives-by-type\">​<span></span></a>Operation parameters are no longer matched to raw primitives by type<span><span>Breaking change</span><span>Fixed</span></span></h3>\n<p>Given <code>operation confirm(OrderId, note : String)</code> and an <code>OrderId</code> in context, the old code would bind the\n<code>OrderId</code>’s value to <code>note</code> — because everything is assignable to <code>String</code>, so a raw primitive parameter\nmatched whatever value happened to be nearby.</p>\n<p>Parameters typed as raw primitives (<code>String</code>, <code>Int</code>, …) or <code>Any</code> are now populated only from an <strong>exact</strong>\ntype match already in context — never an inherited match, never a graph query. Otherwise they resolve to null\nand normal nullability handling applies.</p>\n<p><strong>A default declared in the signature is unaffected</strong>, because it names the parameter it belongs to rather\nthan being matched to it. That remains the supported way to populate a raw primitive.</p>\n<p>If a schema was relying on the old behaviour, the operation will now be invoked with a null (or not invoked at\nall, if the parameter is required). Give the parameter a semantic type, or a default.</p>\n<h3 id=\"query-responses-honour-the-accept-header\"><a href=\"#query-responses-honour-the-accept-header\">​<span></span></a>Query responses honour the Accept header<span><span>Breaking change</span><span>Fixed</span></span></h3>\n<p>HTTP query responses now follow standard content negotiation, in this order:</p>\n<ol role=\"list\">\n<li>An explicit, specific <code>Accept</code> header wins.</li>\n<li>Otherwise the response type’s declared format (<code>@Xml</code>, <code>@Csv</code>) is used.</li>\n<li>Otherwise JSON.</li>\n</ol>\n<p>Previously the model’s declared format always won, so a model annotated <code>@Xml</code> requested with\n<code>Accept: application/json</code> still came back as XML. If you have a client relying on that — sending an <code>Accept</code>\nheader it didn’t mean and getting the model’s format regardless — it will now get what it asked for.</p>\n<p>An absent <code>Accept</code> header is treated as “no preference” rather than being forced to JSON, so it defers to the\nmodel’s declared format. This applies uniformly to the <code>/api/taxiql</code> endpoints and to routed saved queries.\nStreaming responses (SSE and WebSocket) negotiate separately and are unchanged.</p>\n<h3 id=\"api-routes-are-workspace-prefixed-when-workspace-mode-is-enabled\"><a href=\"#api-routes-are-workspace-prefixed-when-workspace-mode-is-enabled\">​<span></span></a>API routes are workspace-prefixed when workspace mode is enabled<span><span>Breaking change</span></span></h3>\n<p>In <code>Workspace</code> or <code>OrgAndWorkspace</code> mode, all workspace-scoped APIs move under <code>/api/{orgId}/{workspaceId}/...</code>,\nand published query endpoints move to <code>/api/{orgId}/{workspaceId}/q/...</code>.</p>\n<p>In <code>None</code> mode — the default — legacy routes such as <code>/api/schema/types</code> and <code>/api/q/myQuery</code> continue to work\nexactly as before. This includes the anonymous-access security matcher, which now picks the pattern matching\nthe active mode.</p>\n<h3 id=\"smaller-breaks\"><a href=\"#smaller-breaks\">​<span></span></a>Smaller breaks<span><span>Breaking change</span></span></h3>\n<ul role=\"list\">\n<li><strong>The auth-token listing API changed shape.</strong> <code>GET /tokens</code> returned a flattened\n<code>Map&lt;ServiceName, List&lt;AuthScheme&gt;&gt;</code>. It now returns a row-shaped listing that preserves the package each\nentry came from, plus a sibling field carrying per-package parse errors.</li>\n<li><strong>Git credentials in <code>workspace.conf</code> are now secret references.</strong> Inline <code>GitCredentials</code> / <code>GitSshAuth</code>\nare replaced by a <code>GitAuthentication</code> reference that points at a secret name resolved through the workspace’s\nsecrets manager at fetch time.</li>\n<li><strong>SOAP responses are parsed by JAXB element names.</strong> Responses are read using <code>@XmlElement</code> metadata and\nfield access rather than by introspecting CXF’s generated Java getters. This fixes acronym and casing\nmangling (<code>getSISOCode</code> was becoming <code>SISOCode</code> rather than <code>sISOCode</code>), but if you had types hand-shaped to\nmatch the old mangled names, they now need to match the WSDL names.</li>\n<li><strong>UI routes renamed</strong>, with redirects in place: <code>/stubs</code> → <code>/local-environment</code>, <code>/catalog/diagram</code> →\n<code>/mesh</code>, <code>/workspace</code> → <code>/workspaces</code>. The old query editor at <code>/query/editor</code> is replaced by\n<code>/code-editor</code>.</li>\n<li><strong>New privileges.</strong> <code>LIST_SECRETS</code> and <code>EDIT_SECRETS</code> were added and are granted to the admin and platform\nmanager roles in the bundled default role definitions. Deployments that already have a <code>roles.conf</code> on disk\nwill not pick these up automatically — Orbital only writes the defaults when the file is absent. Add them by\nhand if you want the secrets UI available to existing roles.</li>\n</ul>\n<div><span></span><span></span></div>\n<h2 id=\"workspaces-and-organisations\"><a href=\"#workspaces-and-organisations\">​<span></span></a>Workspaces and organisations<span><span>New</span></span></h2>\n<p>A workspace is a named context that compiles to a schema. Everything schema-driven in Orbital — queries,\nstreams, published endpoints, the catalog, the search index — operates within one.</p>\n<p>Typical uses:</p>\n<ul role=\"list\">\n<li><strong>Team isolation</strong> — each team owns a workspace with its own services and types.</li>\n<li><strong>Environment separation</strong> — production, staging and development in one cluster.</li>\n<li><strong>Safe experimentation</strong> — test a new version of a service in a workspace without touching the others.</li>\n</ul>\n<p>An <strong>organisation</strong> is the tenant above that. A cluster serves one or more organisations; on-prem deployments\ntypically have one, and a default org is created on first boot if none exists.</p>\n<h3 id=\"turning-it-on\"><a href=\"#turning-it-on\">​<span></span></a>Turning it on</h3>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>vyne</span><span>:</span></span><span><span>  </span><span>toggles</span><span>:</span></span><span><span>    </span><span>workspace-mode</span><span>:</span><span> OrgAndWorkspace</span></span></code></pre></div></div>\n<p>When enabled, a combined org-and-workspace selector appears in the header. Collapsed, it shows the active\nworkspace above the active organisation; expanded, it lists the workspaces in the org, with a nested\n<em>Switch organisation</em> submenu and management actions. In <code>Workspace</code> mode the org half is hidden.</p>\n<p>Every page — catalog, code editor, projects, activity — is scoped to the active workspace, and the URL\nreflects it.</p>\n<h3 id=\"where-workspace-config-lives\"><a href=\"#where-workspace-config-lives\">​<span></span></a>Where workspace config lives</h3>\n<p>Two things need storing: which orgs and workspaces exist, and what projects make up each workspace. Both are\nconfigured under <code>vyne.orgs</code>:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>vyne</span><span>:</span></span><span><span>  </span><span>orgs</span><span>:</span></span><span><span>    </span><span>store</span><span>:</span><span> FILE                 </span><span># FILE | IN_MEMORY | DATABASE</span></span><span><span>    </span><span>config-path</span><span>:</span><span> ./orbital_data</span></span></code></pre></div></div>\n<div><table><thead><tr><th>Backend</th><th>Best for</th><th>Writable from the UI?</th></tr></thead><tbody><tr><td><code>FILE</code> (default)</td><td>Local dev, single-node on-prem</td><td>Yes</td></tr><tr><td><code>IN_MEMORY</code></td><td>Ephemeral environments, tests</td><td>Yes, until restart</td></tr><tr><td><code>DATABASE</code></td><td>Multi-node and cloud deployments</td><td>Yes</td></tr></tbody></table></div>\n<p>The file backend reads an <code>organisations.conf</code> from <code>config-path</code>, with one <code>workspace.conf</code> per workspace\nunderneath:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>orbital_data/</span></span><span><span>  organisations.conf</span></span><span><span>  workspaces/</span></span><span><span>    acme-bank/</span></span><span><span>      production/workspace.conf</span></span><span><span>      analytics/workspace.conf</span></span></code></pre></div></div>\n<p>The database backend stores the same HOCON content as text blobs in <code>org_config</code>, <code>workspace_config_entry</code> and\n<code>workspace_config</code>, using Spring Data repositories over Orbital’s existing datasource. Because the stored\ncontent is byte-identical to the file format, you can move between backends without transforming anything.</p>\n<p>Workspace config also materialises back to disk on boot, so a cloud deployment with a populated database and a\nfresh disk rehydrates correctly. The file backend leaves hand-edited content alone.</p>\n<aside role=\"note\"><p><span>Git-backed org config</span></p><p>A <code>GIT</code> store type is declared in the enum but is not implemented in this milestone. Use <code>FILE</code> or <code>DATABASE</code>.</p></aside>\n<h3 id=\"what-became-workspace-aware\"><a href=\"#what-became-workspace-aware\">​<span></span></a>What became workspace-aware</h3>\n<p>The bulk of the work in 0.39 was making everything downstream of the schema respect workspace boundaries. Things\nthat used to be global singletons and are now per-workspace:</p>\n<ul role=\"list\">\n<li><strong>Connections and invokers.</strong> Every connection-based invoker (JDBC, Kafka, MongoDB, AWS, Azure, Hazelcast,\nSOAP, HTTP) is built per workspace through an <code>InvokerFactory</code>, from that workspace’s own connection registry.</li>\n<li><strong><code>services.conf</code> and <code>auth.conf</code>.</strong> Service discovery and outbound auth resolve through per-workspace\nregistries, so HOCON substitution sees that workspace’s own environment variables. Previously a single global\nregistry meant <code>auth.conf</code> files discovered through a workspace’s project <code>additionalSources</code> were silently\nignored, and outbound HTTP calls went out unauthenticated.</li>\n<li><strong>Hazelcast state.</strong> Operation caches, state stores and stream topics are all namespaced by workspace, so\nresults can’t leak across boundaries.</li>\n<li><strong>The search index.</strong> Lucene indices are per workspace, stored under <code>{basePath}/{orgId}/{workspaceId}/</code>,\nrebuilt only for the workspace whose schema changed.</li>\n<li><strong>Query history.</strong> Every history table is stamped and filtered by org and workspace.</li>\n<li><strong>The language server.</strong> LSP sessions resolve the workspace’s live schema, and schema edits are delivered to\nthe sessions bound to that workspace.</li>\n<li><strong>WebSocket streams.</strong> Schema notifications, query status, stream results and stub updates all reconnect when\nyou switch workspace, and are filtered to the workspace you’re connected to.</li>\n<li><strong>Query routes, scheduled queries and persistent streams.</strong> These maintain cross-workspace indices and now\nsubscribe to schema changes from every workspace, including ones created at runtime.</li>\n</ul>\n<p>Workspaces created through the API or by dropping a directory on disk are registered live — no restart needed\nfor schema compilation, search indexing or route registration.</p>\n<h3 id=\"org-scoped-authorization\"><a href=\"#org-scoped-authorization\">​<span></span></a>Org-scoped authorization<span><span>New</span><span>Security</span></span></h3>\n<p><code>@RequiresOrgPrivilege</code> replaces <code>@PreAuthorize(&quot;hasAuthority(...)&quot;)</code> across roughly 150 production endpoints.\nIt resolves the request’s <code>{orgId}</code> path variable and delegates the decision to an <code>OrgAuthorityService</code>, which\nhas two implementations:</p>\n<ul role=\"list\">\n<li>The default is org-blind and checks the flat authority set, preserving behaviour for single-org and\nnon-PropelAuth deployments.</li>\n<li>When <code>vyne.security.open-idp.roles.format=propelauth</code>, per-org roles are read from the JWT’s\n<code>org_id_to_org_member_info</code> claim.</li>\n</ul>\n<p>This unblocks users who belong to multiple organisations, who previously hit\n<em>“You are a member of multiple organisations”</em>.</p>\n<p>Org membership is now enforced on the workspace endpoints themselves. Previously any authenticated user could\nlist or create workspaces in any tenant by putting that tenant’s slug in the URL. Organisations are also\nauto-provisioned from JWT claims on first sign-in, so workspace creation works on a fresh deployment.</p>\n<div><span></span><span></span></div>\n<h2 id=\"copilot\"><a href=\"#copilot\">​<span></span></a>Copilot<span><span>New</span></span></h2>\n<p>Copilot is a chat interface to your data mesh. It runs in two modes:</p>\n<ul role=\"list\">\n<li><strong>Ask</strong> — natural-language questions answered by generating and running a TaxiQL query against the current\nworkspace. <em>“What’s Jim Patterson’s account balance?”</em>, or <em>“write me a query that joins orders to\ncustomers”</em>.</li>\n<li><strong>Build</strong> — an agent with file-editing tools that works on your Taxi projects, streaming its edits directly\ninto the code editor beside the conversation.</li>\n</ul>\n<p>The mode is chosen before you send the first message, and pinned for the life of the conversation — reopening\na conversation restores the mode it was started with.</p>\n<p>Conversations are persisted with their history, can be renamed, and are listed in a sidebar. File uploads are\nsupported and scoped to the workspace. In Build mode, edits stream into Monaco models as they arrive, so you\nwatch the agent write.</p>\n<h3 id=\"configuring-it\"><a href=\"#configuring-it\">​<span></span></a>Configuring it</h3>\n<p>Copilot is a separate service. Point Orbital at it and turn on the toggle:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>vyne</span><span>:</span></span><span><span>  </span><span>toggles</span><span>:</span></span><span><span>    </span><span>copilot-enabled</span><span>:</span><span> </span><span>true</span></span><span><span>  </span><span>copilot</span><span>:</span></span><span><span>    </span><span>endpoint-url</span><span>:</span><span> http</span><span>:</span><span>//localhost</span><span>:</span><span>9028</span></span><span><span>    </span><span>upload-store</span><span>:</span><span> local</span></span><span><span>    </span><span>local-store-path</span><span>:</span><span> ./orbital_data/copilot/uploads</span></span></code></pre></div></div>\n<p>Requests to the Copilot service carry the caller’s JWT, and all calls are scoped to the current org and\nworkspace.</p>\n<h3 id=\"mcp-server\"><a href=\"#mcp-server\">​<span></span></a>MCP server<span><span>New</span></span></h3>\n<p>Orbital exposes a <a href=\"https://modelcontextprotocol.io\" target=\"_blank\" rel=\"noreferrer\">Model Context Protocol</a> server, so Claude, Cursor, IDE\nagents and your own tooling can ask the same questions a Copilot user can.</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>vyne</span><span>:</span></span><span><span>  </span><span>toggles</span><span>:</span></span><span><span>    </span><span>mcp-server-enabled</span><span>:</span><span> </span><span>true</span></span></code></pre></div></div>\n<p>One endpoint is mounted per workspace:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>/api/{orgId}/{workspaceId}/mcp</span></span></code></pre></div></div>\n<p>Mounting it under the workspace path means it inherits the existing org-membership gate from the security\nfilter chain. No tool takes <code>orgId</code> or <code>workspaceId</code> as an argument — the URL is the only source, so an MCP\nclient can’t reach across into another tenant’s data by changing a parameter.</p>\n<p>The server exposes a single <code>ask</code> tool:</p>\n<div><table><thead><tr><th>Field</th><th>Description</th></tr></thead><tbody><tr><td><code>question</code></td><td>The question, in natural language</td></tr><tr><td><code>execute</code></td><td>Run the generated query and return rows, or just show the query that would run</td></tr></tbody></table></div>\n<p>It answers with the natural-language <code>answer</code>, the generated <code>query</code>, a <code>queryHistoryId</code> you can use to\ninspect the run in Orbital’s history, the result <code>rows</code>, and any <code>queryError</code> or <code>diagnostics</code>.</p>\n<p>The tool description and the server’s MCP <code>instructions</code> are generated from the workspace’s own schema, so\nclients know what data they’re pointed at.</p>\n<p>Authentication accepts a PropelAuth API key presented the standard way — <code>Authorization: Bearer &lt;token&gt;</code> —\nwhich is what MCP clients send. Orbital now discriminates structurally: a bearer credential shaped like a JWT\nis validated as one, anything else is validated as an opaque API key. Previously an API key had to be sent\n<em>without</em> the <code>Bearer</code> prefix, which MCP clients can’t do.</p>\n<div><span></span><span></span></div>\n<h2 id=\"the-new-code-editor\"><a href=\"#the-new-code-editor\">​<span></span></a>The new code editor<span><span>New</span></span></h2>\n<p>The old single-query editor is replaced by a multi-file editor at <code>/code-editor</code>.</p>\n<ul role=\"list\">\n<li><strong>A real file tree.</strong> It walks the project on disk, so <code>taxi.conf</code>, READMEs, <code>connections.conf</code>, nebula\nscripts and everything else is visible — not just <code>.taxi</code> files. Content is fetched lazily when you click.\nPer-file error counts show in the tree.</li>\n<li><strong>Split panels.</strong> Editors, query results and query history live in dockable panels you can arrange, and the\nlayout persists across refreshes — keyed by workspace, so switching org or workspace doesn’t restore the\nwrong location’s panels.</li>\n<li><strong>File lifecycle.</strong> Right-click to rename or delete. Both flow through the same compile-after-change pipeline\nas content edits, with atomic moves for renames.</li>\n<li><strong>Save anything.</strong> Saving no longer runs every source in the package through the Taxi compiler and rejects\nthe write on any error. Non-Taxi files are excluded from compilation entirely, and Taxi files with\ncompilation errors save anyway, with the errors surfaced in the UI. The editor’s job is to write what you\ntyped.</li>\n<li><strong>Language support</strong> is auto-detected from the file extension. Markdown renders. Unmapped types open as\nplain text instead of failing.</li>\n</ul>\n<p>Two long-standing editor bugs are fixed. Editing or saving a file used to produce\n<code>Symbol X is already declared</code> errors for every symbol in the file, caused by the same file being registered\nunder two different URIs — once through the language server and once through the schema editor’s in-memory\nedit application. And edits to files outside <code>src/</code> were being nested under <code>src/</code>, while edits already\naddressed with a <code>src/</code> prefix were double-nested at <code>src/src/...</code>.</p>\n<div><span></span><span></span></div>\n<h2 id=\"secrets-management\"><a href=\"#secrets-management\">​<span></span></a>Secrets management<span><span>New</span></span></h2>\n<p>Secrets are now first-class. Reference one anywhere in your config the same way you’d reference an environment\nvariable:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>connections.jdbc.warehouse </span><span>{</span></span><span><span>   </span><span>url</span><span>:</span><span>      </span><span>&quot;jdbc:postgresql://warehouse.example.com:5432/prod&quot;</span></span><span><span>   </span><span>password</span><span>:</span><span> $</span><span>{</span><span>secrets.DB_PASSWORD</span><span>}</span></span><span><span>}</span></span></code></pre></div></div>\n<p>Real values never enter the merged HOCON config. The placeholder resolves to an opaque token that the consumer\nresolves at the point of use — at connection time, at outbound-request time, at git-fetch time — so a secret\nlives in a call frame and never in long-lived state. Rotating a secret takes effect without a restart.</p>\n<h3 id=\"backends\"><a href=\"#backends\">​<span></span></a>Backends</h3>\n<p>Pick one with <code>vyne.secrets.backend</code>:</p>\n<div><table><thead><tr><th>Value</th><th>Backend</th></tr></thead><tbody><tr><td><code>InMemory</code> (default)</td><td>In-process. Tests and demos only.</td></tr><tr><td><code>File</code></td><td>Local file, age-encrypted (X25519 + ChaCha20-Poly1305). Dev only — the key sits next to the data.</td></tr><tr><td><code>Infisical</code></td><td><a href=\"https://infisical.com\" target=\"_blank\" rel=\"noreferrer\">Infisical</a>, cloud or self-hosted, via universal auth</td></tr><tr><td><code>Aws</code></td><td><a href=\"https://aws.amazon.com/secrets-manager/\" target=\"_blank\" rel=\"noreferrer\">AWS Secrets Manager</a></td></tr><tr><td><code>Vault</code></td><td><a href=\"https://www.vaultproject.io/\" target=\"_blank\" rel=\"noreferrer\">HashiCorp Vault</a> KV v2</td></tr><tr><td><code>Gcp</code></td><td><a href=\"https://cloud.google.com/secret-manager\" target=\"_blank\" rel=\"noreferrer\">Google Cloud Secret Manager</a></td></tr></tbody></table></div>\n<p>Each backend maps Orbital’s scope hierarchy onto its own naming rules — paths in Vault and Infisical, flat\nprefixed names in AWS, <code>__</code>-separated segments in GCP, whose names can’t contain slashes.</p>\n<h3 id=\"scopes\"><a href=\"#scopes\">​<span></span></a>Scopes</h3>\n<div><table><thead><tr><th>Scope</th><th>Visible to</th></tr></thead><tbody><tr><td><code>Server</code></td><td>every workspace on this server</td></tr><tr><td><code>Organisation</code></td><td>every workspace in that organisation</td></tr><tr><td><code>Workspace</code></td><td>only that workspace</td></tr></tbody></table></div>\n<p>Reads cascade — workspace, then organisation, then server — and the narrower scope wins on a name collision.\nThere is no global namespace that bypasses scoping.</p>\n<h3 id=\"managing-them\"><a href=\"#managing-them\">​<span></span></a>Managing them</h3>\n<p>Secrets are managed in the UI under <strong>Secrets</strong>, and over REST:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>GET    /api/{orgId}/{workspaceId}/secrets</span></span><span><span>GET    /api/{orgId}/{workspaceId}/secrets/{scope}</span></span><span><span>POST   /api/{orgId}/{workspaceId}/secrets/{scope}</span></span><span><span>PUT    /api/{orgId}/{workspaceId}/secrets/{scope}/{name}</span></span><span><span>DELETE /api/{orgId}/{workspaceId}/secrets/{scope}/{name}</span></span><span><span>POST   /api/{orgId}/{workspaceId}/secrets/refresh</span></span></code></pre></div></div>\n<p>Reads need <code>LIST_SECRETS</code>, writes need <code>EDIT_SECRETS</code>. <strong>No endpoint ever returns a secret value</strong> — responses\ncarry name, scope, description, last-updated and version only. Server-scoped secrets are listable but\noperator-managed: writes to that scope are rejected with a 403.</p>\n<p>A caller at <code>/api/acme/prod/...</code> can only touch <code>Workspace(acme/prod)</code> and <code>Organisation(acme)</code> — not another\norg’s or another workspace’s secrets.</p>\n<p>Every backend is wrapped in a caching decorator with a 15-minute TTL (configurable via\n<code>vyne.secrets.cache.ttl</code> and <code>.max-size</code>), write-through invalidation, and the <code>refresh</code> endpoint above for\nforcing a reload after rotating a secret out of band.</p>\n<h3 id=\"git-authentication\"><a href=\"#git-authentication\">​<span></span></a>Git authentication<span><span>New</span></span></h3>\n<p>Adding a git project now captures credentials — none, username/password, token, or SSH key — as references to\nsecrets rather than as credentials embedded in <code>workspace.conf</code> and travelling through git. The UI rejects\nplaintext entries at submit, since they would defeat the point.</p>\n<p>Credentials resolve inside JGit’s <code>configure(transport)</code> call. SSH keys load as bytes, so no key is ever\nwritten to a temp file.</p>\n<p>See <a href=\"https://orbitalhq.com/docs/deploying/managing-secrets\">managing secrets</a> for the full setup.</p>\n<div><span></span><span></span></div>\n<h2 id=\"data-validation\"><a href=\"#data-validation\">​<span></span></a>Data validation<span><span>New</span></span></h2>\n<p>You can now declare validation rules against your types, and control what happens when data breaks them.\nBecause rules are declared against <strong>types</strong>, you define them once and they’re enforced everywhere that type\nappears, whichever service the data came from.</p>\n<p>Three parts: <strong>declare</strong> rules with annotations, <strong>activate</strong> them with <code>@Valid</code>, <strong>decide</strong> what happens with\n<code>@OnValidationFailure</code>.</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>import</span><span> com</span><span>.</span><span>orbitalhq</span><span>.</span><span>validation</span><span>.</span><span>NotEmpty</span></span><span><span>import</span><span> com</span><span>.</span><span>orbitalhq</span><span>.</span><span>validation</span><span>.</span><span>Valid</span></span><span>\n</span><span><span>@</span><span>NotEmpty</span></span><span><span>type</span><span> </span><span>Name</span><span> </span><span>inherits</span><span> </span><span>String</span></span><span>\n</span><span><span>@</span><span>Valid</span></span><span><span>model</span><span> </span><span>Person</span><span> </span><span>{</span></span><span><span>   name</span><span> </span><span>:</span><span> Name</span></span><span><span>}</span></span></code></pre></div></div>\n<p>A <code>Person</code> arriving as <code>{ &quot;name&quot;: &quot;&quot; }</code> produces one violation — rule <code>com.orbitalhq.validation.NotEmpty</code>, at\npath <code>name</code>, severity <code>Error</code> — and by default rejects the query with an HTTP 400. Remove the <code>@Valid</code> and the\nsame data flows through untouched. Rules do nothing until something activates them.</p>\n<h3 id=\"the-built-in-rules\"><a href=\"#the-built-in-rules\">​<span></span></a>The built-in rules</h3>\n<p><code>@NotEmpty</code>, <code>@MinLength</code>, <code>@MaxLength</code>, <code>@Pattern</code>, <code>@Min</code>, <code>@Max</code>, <code>@MultipleOf</code>, <code>@MinItems</code>, <code>@MaxItems</code>,\n<code>@UniqueItems</code>, <code>@Email</code>, <code>@Uuid</code>. That’s feature parity with OpenAPI’s schema validations. Every rule takes an\noptional <code>message</code> and <code>severity</code> (<code>Info</code>, <code>Warning</code> or <code>Error</code>).</p>\n<p>Rules can go on a type, a field, or a query parameter, and they’re inherited — a rule on <code>Name</code> applies to\nevery type inheriting from it.</p>\n<p>Two rule ids are produced by the engine rather than declared: <code>NotNull</code>, when a non-nullable field inside a\n<code>@Valid</code> scope holds a null, and <code>ParseFailure</code>, when a value can’t be parsed into its declared type.</p>\n<aside role=\"note\"><p><span>@Valid also turns on nullability enforcement</span></p><p>Type-system nullability has historically been declared but not enforced. Inside a <code>@Valid</code> scope it is. This\nis opt-in — nothing changes for schemas that don’t use <code>@Valid</code> — but expect to find violations the first\ntime you switch it on for an existing model.</p></aside>\n<h3 id=\"deciding-what-happens\"><a href=\"#deciding-what-happens\">​<span></span></a>Deciding what happens</h3>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>@</span><span>OnValidationFailure</span><span>(</span><span>action </span><span>=</span><span> ValidationAction</span><span>.</span><span>Warn</span><span>,</span><span> threshold </span><span>=</span><span> Severity</span><span>.</span><span>Error</span><span>)</span></span><span><span>query</span><span> </span><span>GetOrder</span><span> </span><span>{</span></span><span><span>   </span><span>find</span><span> </span><span>{</span><span> Order</span><span> </span><span>}</span></span><span><span>}</span></span></code></pre></div></div>\n<div><table><thead><tr><th>Action</th><th>Behaviour</th></tr></thead><tbody><tr><td><code>Reject</code></td><td>Fails the request with an HTTP 400 and a structured violation report</td></tr><tr><td><code>Warn</code></td><td>Data passes through. Violations are still logged and reported</td></tr><tr><td><code>Drop</code></td><td>Skips the offending record and keeps going. Never silent — every drop is reported</td></tr></tbody></table></div>\n<p><code>threshold</code> sets the minimum severity that triggers the action. <strong>All violations are reported regardless of\nthreshold</strong> — the threshold only decides which are severe enough to act on. That pairs well with per-rule\nseverity: mark rules you’re not yet confident about as <code>Warning</code>, watch them in your logs, promote them to\n<code>Error</code> once you trust them.</p>\n<p>Defaults are per query kind: <code>find</code> rejects at <code>Error</code>, <code>stream</code> drops at <code>Error</code>. Query arguments always\nreject — dropping a record makes no sense when the record is the caller’s own input.</p>\n<p>Violation paths are precise: <code>cast.actors[1].actor.name</code>, not just “somewhere in the response”.</p>\n<p>Validation runs on query arguments, on data returned by services, and on streaming pipeline inputs.</p>\n<p>See <a href=\"https://orbitalhq.com/docs/data-validation/data-validation\">validating data</a> and the\n<a href=\"https://orbitalhq.com/docs/data-validation/validation-rules\">rule reference</a>.</p>\n<div><span></span><span></span></div>\n<h2 id=\"data-sources\"><a href=\"#data-sources\">​<span></span></a>Data sources</h2>\n<h3 id=\"oracle\"><a href=\"#oracle\">​<span></span></a>Oracle<span><span>New</span></span></h3>\n<p>Orbital now ships an Oracle driver, with full write support: <code>Insert</code> and generated-primary-key <code>Upsert</code> use\n<code>INSERT ... RETURNING</code>, <code>Update</code> runs per-row batches, and a user-supplied-PK <code>Upsert</code> uses <code>MERGE</code>. Oracle’s\n<code>MERGE</code> can’t return rows, so the invoker echoes the input back, matching how the Postgres upsert behaves.</p>\n<p>Table generation accounts for Oracle lacking <code>CREATE INDEX IF NOT EXISTS</code> and needing explicit lengths on\nindexed <code>VARCHAR2</code> / <code>CLOB</code> columns. jOOQ’s dialect is resolved by probing the live connection rather than\nassuming a version, so Orbital doesn’t emit <code>CREATE TABLE IF NOT EXISTS</code> (23ai and later only) against an\nolder database. The probe is cached per connection.</p>\n<p>Metadata reads are scoped to the connecting user’s schema. Oracle has no schema concept separate from the\nuser, so without that scoping Orbital crawled <code>SYS</code>, <code>SYSTEM</code> and every other user’s schema.</p>\n<h3 id=\"database-metadata-is-read-directly-and-scoped\"><a href=\"#database-metadata-is-read-directly-and-scoped\">​<span></span></a>Database metadata is read directly, and scoped<span><span>Improved</span><span>Fixed</span></span></h3>\n<p>Table introspection — the connection UI’s table list, and the schema generator behind <em>import from database</em> —\nwas built on SchemaCrawler, which required a full catalog crawl. On a warehouse that’s slow to the point of\nunusable, and it had no plugin support for Snowflake or Databricks.</p>\n<p>It’s been replaced with targeted <code>DatabaseMetaData</code> calls. What that changes for you:</p>\n<ul role=\"list\">\n<li><strong>The reactive server no longer stalls.</strong> <code>listConnectionTables</code> and <code>getTableMetadata</code> ran their blocking\nJDBC calls on the Netty event loop, which starved the whole server until it was restarted. They now run on\na bounded elastic scheduler.</li>\n<li><strong>Listing tables returns names only.</strong> Column, primary-key and index metadata were an extra round-trip per\ntable, on a screen that only needs names. They’re opt-in now, via <code>includeColumns</code> / <code>includeIndexes</code> query\nparameters on the tables endpoint.</li>\n<li><strong>Generating Taxi for one table is independent of database size.</strong> It reads the tables you asked for plus\nthe ones their foreign keys reference, one level deep, rather than crawling the whole schema.</li>\n<li><strong>Better type coverage.</strong> <code>CLOB</code> maps to <code>String</code> instead of failing generation outright, and Postgres\ndomain types map to <code>Any</code> rather than erroring.</li>\n</ul>\n<h3 id=\"databricks-and-snowflake\"><a href=\"#databricks-and-snowflake\">​<span></span></a>Databricks and Snowflake<span><span>New</span></span></h3>\n<p><strong>Databricks</strong> is new. Hostname, port, HTTP path and token are the user-facing parameters; <code>AuthMech=3</code> and\n<code>UID=token</code> are baked into the URL template so you don’t see them.</p>\n<p><strong>Snowflake</strong> existed as a twelve-line shell that had never been used in anger, with its JDBC driver commented\nout of the build because the fat jar broke shading. It’s been rebuilt: region is now an optional URL parameter,\nand the thin driver replaces the fat one — no shading problems, no broken native build.</p>\n<p>Neither platform supports the <code>ON CONFLICT</code> syntax the other drivers use for upserts, so both build a\n<code>MERGE INTO</code> statement instead. That needs something to match existing rows on, so when a primary key column\nisn’t among the written fields — a <code>@GeneratedId</code> key, for instance — the write falls back to a plain\n<code>INSERT</code> and logs a warning. It’s a warning rather than a silent fallback because rows can duplicate under an\n<code>@UpsertOperation</code> when it happens.</p>\n<p>Driver-specific column unwrapping moved off the base JDBC invoker onto the driver itself, so Postgres owns its\n<code>PGobject</code> / <code>PgArray</code> handling and any driver returning its own wrapper types can be supported cleanly.</p>\n<h3 id=\"mongodb\"><a href=\"#mongodb\">​<span></span></a>MongoDB<span><span>New</span></span></h3>\n<p>MongoDB connections can now be created and tested from the UI. The driver was already listed in the connections\neditor, but test and create posted nowhere — the UI had no URL mapping for NoSQL connectors and the backend had\nno endpoint. Both now exist, and MongoDB has a health-check provider like the other connectors.</p>\n<h3 id=\"connection-failures-surface-properly\"><a href=\"#connection-failures-surface-properly\">​<span></span></a>Connection failures surface properly<span><span>Fixed</span></span></h3>\n<p>A database-backed saved query invoked through a routed HTTP endpoint used to return <code>200 OK</code> with an empty body\nwhen its connection was undefined or unreachable — indistinguishable from a query that legitimately matched\nnothing.</p>\n<p>Connectors now raise typed exceptions whose HTTP status comes from the exception itself:</p>\n<div><table><thead><tr><th>Condition</th><th>Status</th></tr></thead><tbody><tr><td>Schema references an undefined connection</td><td>500</td></tr><tr><td>Connection defined but upstream unreachable</td><td>502</td></tr></tbody></table></div>\n<p>JDBC connection failures are also reported as trace events, so they show up in the query profile rather than\nvanishing.</p>\n<h3 id=\"deletes-and-native-sql\"><a href=\"#deletes-and-native-sql\">​<span></span></a>Deletes and native SQL<span><span>New</span></span></h3>\n<p><code>@DeleteOperation</code> gives you type-safe deletes through TaxiQL. A single <code>@Id</code> renders <code>WHERE pk IN (...)</code>,\ncomposite keys use row-value <code>IN</code>, and statements are chunked at 500 rows to stay under Oracle’s IN-list cap.\nValues are always bound, never inlined. The operation returns a <code>deletedCount</code>.</p>\n<p><code>@SqlQuery</code> lets you write the SQL yourself — joins, aggregations, dialect-specific statements — for the cases\nwhere the generated query isn’t what you want. One annotation covers reads and native DML, split by taxi’s\n<code>write</code> keyword. Reads map joined and aggregated rows onto plain models by column name or alias, with no\n<code>@Table</code> needed; writes return the affected-row count.</p>\n<p><code>:param</code> placeholders bind as real prepared-statement parameters, validated against the operation’s parameters\nbefore execution. Values never touch the SQL text.</p>\n<p>Both were <a href=\"https://orbitalhq.com/changelog/2026-07-16-jdbc-deletes-and-native-sql\">announced in July</a>, and this is the first build\nthey ship in.</p>\n<h3 id=\"also\"><a href=\"#also\">​<span></span></a>Also</h3>\n<ul role=\"list\">\n<li>Overriding a service in <code>services.conf</code> can now downgrade from <code>https</code> to <code>http</code>, which it previously\nrefused to do.</li>\n</ul>\n<div><span></span><span></span></div>\n<h2 id=\"query-engine\"><a href=\"#query-engine\">​<span></span></a>Query engine</h2>\n<h3 id=\"collection-options-limit-sort-paginate-and-dedupe\"><a href=\"#collection-options-limit-sort-paginate-and-dedupe\">​<span></span></a>Collection options: limit, sort, paginate and dedupe<span><span>New</span></span></h3>\n<p>You can shape the results of a query directly in TaxiQL — limiting how many rows come back, paginating through\nthem, sorting them, and removing duplicates:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> Person</span><span>[</span><span>]</span><span>(</span><span> CountryCode </span><span>==</span><span> </span><span>&quot;GB&quot;</span><span>,</span><span> orderBy</span><span>:</span><span> DateOfBirth</span><span> desc</span><span>,</span><span> offset</span><span>:</span><span> </span><span>20</span><span>,</span><span> limit</span><span>:</span><span> </span><span>10</span><span> </span><span>)</span><span> </span><span>}</span></span></code></pre></div></div>\n<p><code>limit</code>, <code>offset</code>, <code>orderBy</code> and <code>uniqueBy</code> are the standard database-style controls, and they work on <strong>any</strong>\nquery, against any source — not just databases.</p>\n<p>Sort by more than one field, and paginate:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> Person</span><span>[</span><span>]</span><span>(</span><span> orderBy</span><span>:</span><span> </span><span>[</span><span>CountryCode asc</span><span>,</span><span> DateOfBirth desc</span><span>]</span><span>,</span><span> offset</span><span>:</span><span> </span><span>40</span><span>,</span><span> limit</span><span>:</span><span> </span><span>20</span><span> </span><span>)</span><span> </span><span>}</span></span></code></pre></div></div>\n<p>They work on projections too, including nested collections, which is handy for trimming child records per\nparent:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> Customer</span><span>[</span><span>]</span><span> </span><span>}</span><span> </span><span>as</span><span> </span><span>{</span></span><span><span>  name</span><span> </span><span>:</span><span> CustomerName</span></span><span><span>  </span><span>// Only the 20 most recent transactions per customer</span></span><span><span>  transactions</span><span> </span><span>:</span><span> Transaction</span><span>[</span><span>]</span><span>(</span><span> limit</span><span>:</span><span> </span><span>20</span><span>,</span><span> orderBy</span><span>:</span><span> TransactionDate</span><span> desc </span><span>)</span></span><span><span>}</span><span>[</span><span>]</span></span></code></pre></div></div>\n<p>Values can be literals or query parameters, so <code>limit: maxRows</code> works when <code>maxRows</code> is a query argument.</p>\n<p><strong>Pushdown is decided per option, per data source.</strong> When a source can apply an option itself, Orbital pushes\nthe work down to it; when it can’t, Orbital does it after fetching. Either way you get the same answer, and\npushdown only happens when it can’t change the result — if a source can’t sort, Orbital won’t push a <code>limit</code>\nbelow the missing sort, because limiting before sorting returns the wrong rows.</p>\n<div><table><thead><tr><th>Source</th><th>Pushed down</th></tr></thead><tbody><tr><td>SQL databases (Postgres, MySQL, MSSQL, Oracle, Redshift, Snowflake, Databricks)</td><td><code>limit</code>, <code>offset</code>, and <code>orderBy</code> when every sort term resolves to a single column — rendered in your driver’s dialect</td></tr><tr><td>MongoDB</td><td><code>limit</code>, <code>offset</code> and <code>orderBy</code>, as <code>sort</code> / <code>skip</code> / <code>limit</code></td></tr><tr><td>Hazelcast</td><td><code>limit</code>, as a key-ordered page</td></tr><tr><td>REST APIs, Kafka, everything else</td><td>Nothing — Orbital applies all of it after fetching</td></tr></tbody></table></div>\n<p><code>uniqueBy</code> is never pushed down; it’s always applied by Orbital. Operations annotated with <code>@SqlQuery</code> opt out\nof pushdown entirely, since options can’t be spliced into SQL you wrote by hand — they’re applied after the\nfetch instead.</p>\n<p>On streaming queries, <code>limit</code> is supported and completes the stream once it has emitted N items. <code>orderBy</code>,\n<code>offset</code> and <code>uniqueBy</code> are rejected on streams, as is cursor pagination (<code>after</code> / <code>before</code>), which parses but\nis not yet implemented.</p>\n<p>This was <a href=\"https://orbitalhq.com/changelog/2026-07-04-collection-options\">announced in July</a> — 0.39.1-M1 is the first build it ships in.\nFull detail is in the <a href=\"https://orbitalhq.com/docs/querying/collection-options\">collection options docs</a>.</p>\n<h3 id=\"operation-parameters-are-gathered-consistently\"><a href=\"#operation-parameters-are-gathered-consistently\">​<span></span></a>Operation parameters are gathered consistently<span><span>Fixed</span></span></h3>\n<p>Parameters were being gathered in four separate places that disagreed with each other: the mutation path, the\ngraph-search path, the direct-invocation path, and again inside the invoker. The same call could bind a value\nto the wrong parameter on one path, send <code>null</code> on another, and ignore a declared default entirely on a third.\nAn operation could also be invoked with fewer arguments than it declares, because the graph path caught any\ndiscovery failure and quietly dropped that parameter.</p>\n<p>There is now one shared implementation returning an explicit map of parameter to value. Callers differ in what\nthey do when it can’t be satisfied — direct invocation tries another candidate, graph search fails the edge,\nmutation returns a null — but they all gather the same way.</p>\n<p>The operation-result cache is keyed off that map, too. Keyed off a set, as it was,\n<code>getEligibility(orderId=A, lineId=B)</code> and <code>getEligibility(orderId=B, lineId=A)</code> shared a cache entry.</p>\n<p>Nullable parameters that can be constructed from context now are. Previously <code>discover</code> short-circuited to null\nthe moment it saw a nullable parameter, so a nullable request body whose fields were all present in context was\nsent as <code>null</code>.</p>\n<h3 id=\"circular-type-references-no-longer-blow-the-stack\"><a href=\"#circular-type-references-no-longer-blow-the-stack\">​<span></span></a>Circular type references no longer blow the stack<span><span>Fixed</span></span></h3>\n<p>A model whose definition leads back to itself — <code>total : Total = (Total * 1.2)</code>, or a pair of types whose\nexpressions reference each other — recursed until the JVM threw <code>StackOverflowError</code>.</p>\n<p>A closed loop now yields a null carrying the cycle path — <code>Order.total -&gt; Order.total</code>, or <code>A -&gt; B -&gt; A</code> —\nrather than throwing. Circular definitions are now <em>legible</em>, not valid; they remain unsatisfiable.</p>\n<p>This matters more than it looks. <code>StackOverflowError</code> isn’t recoverable the way an exception is: if it lands\nwhile a class is initialising, that class stays unusable for the life of the JVM and every later touch throws\n<code>NoClassDefFoundError</code>. We hit exactly that during a benchmark run — it broke coroutine cancellation\nprocess-wide, with no error naming the cause.</p>\n<h3 id=\"collecting-arrays-across-collection-boundaries\"><a href=\"#collecting-arrays-across-collection-boundaries\">​<span></span></a>Collecting arrays across collection boundaries<span><span>Fixed</span></span></h3>\n<p>A query like <code>find { GivenName[] }</code>, against a response whose <code>givenNames</code> sit two collection layers deep\ninside the returned object, failed with <em>“no data sources can return GivenName[]”</em>. The query graph only had\nscalar attribute edges, so there was no path.</p>\n<p>There’s now an edge from each provided instance to every <code>T[]</code> reachable via a path crossing at least one\ncollection field. Its cost sits between cheap attribute navigation and remote operation invocation, so a direct\nattribute path still wins when one exists, but the planner always prefers in-memory extraction over calling a\nservice again.</p>\n<p>A service returning no matching instances yields an empty <code>T[]</code> rather than failing the query.</p>\n<h3 id=\"query-errors-reach-the-error-view\"><a href=\"#query-errors-reach-the-error-view\">​<span></span></a>Query errors reach the error view<span><span>Fixed</span></span></h3>\n<p>Several places in the engine caught exceptions and returned a null or an empty result without telling anyone,\nso genuine failures never reached the query error view. Failures that are actually meaningful — a nullable\noperation parameter that couldn’t be constructed, unexpected exceptions while searching for a type — now\npublish to the error stream that feeds the errors websocket and query history.</p>\n<p>Routine search backtracking is deliberately left silent. It’s normal control flow, it fires per row during\nprojection, and surfacing it would flood the view with alarming noise.</p>\n<p>Separately, fast-failing queries used to tear down their error stream before the UI could connect to it, so the\nsame query would only intermittently show its errors. The error publisher keeps a 30-second replay buffer for\nexactly this reason; eviction is now deferred past the replay window so a late-connecting websocket can still\ndrain it.</p>\n<h3 id=\"also-in-the-engine\"><a href=\"#also-in-the-engine\">​<span></span></a>Also in the engine<span><span>Fixed</span></span></h3>\n<ul role=\"list\">\n<li>A TaxiQL statement that fails to compile returns <strong>400 Bad Request</strong> rather than 500.</li>\n<li>Failed searches capture where and why they failed, with a readable display of the failure point.</li>\n<li>Graph search error reporting is more informative, and the error message now hints when a <code>find</code> with a\ncontract would be better expressed as a <code>given {}</code>.</li>\n<li>Fact-bag lookups had lost their combine, dedupe and absent semantics when they moved to an <code>Either</code>-based\nAPI: deep searches silently dropped all secondary-bag matches whenever the primary bag had any match, merged\ncollections skipped deduplication, and “absent everywhere” was reported as an empty collection rather than a\nnull — which stopped the object builder falling through to discovery. All three are restored.</li>\n<li>Data policies are no longer applied to primitive types.</li>\n<li>Projections no longer attempt to construct scalar types, and fields carrying expressions are deferred to\nexpression evaluation rather than being built directly.</li>\n<li>Object construction no longer throws when the type is nullable, matching field construction.</li>\n</ul>\n<h3 id=\"performance\"><a href=\"#performance\">​<span></span></a>Performance<span><span>Improved</span></span></h3>\n<p>This milestone adds a benchmark suite and CI guards rather than a broad optimisation pass. <code>query-engine-benchmarks</code>\ncarries seeded synthetic schema and data generators, 13 workloads, 7 JMH micro-benchmarks, and a guard suite\nthat gates merge requests on deterministic metrics — operation invocation counts, allocation budgets,\ncomplexity-class scaling ratios and heap-slope leak checks — while tracking wall-clock time as a nightly trend\nrather than a flaky gate.</p>\n<p>One fix landed alongside it: fact-bag search caching now validates lazily on read against a generation stamp,\nso adding a fact is O(1) rather than doing predicate work proportional to every cached search. Measured 86–93%\noff <code>addFact</code>, with lookup cost flat.</p>\n<p>A full review of the engine’s performance characteristics is written up internally. The remaining findings are\nthe fix programme for subsequent releases, and each has a matching benchmark guard waiting to be switched on.</p>\n<div><span></span><span></span></div>\n<h2 id=\"ui\"><a href=\"#ui\">​<span></span></a>UI<span><span>Improved</span></span></h2>\n<p>The navigation has been reorganised:</p>\n<div><table><thead><tr><th>Was</th><th>Now</th></tr></thead><tbody><tr><td>Query editor</td><td><strong>Code editor</strong> — the new multi-file editor</td></tr><tr><td>Query history</td><td><strong>Activity</strong> — running queries and history in one paginated view</td></tr><tr><td>Authentication</td><td><strong>Secrets</strong></td></tr><tr><td>Stub Servers</td><td><strong>Local environments</strong></td></tr><tr><td>Catalog → Diagram tab</td><td><strong>Mesh</strong> — promoted to a top-level item</td></tr><tr><td>—</td><td><strong>Copilot</strong></td></tr></tbody></table></div>\n<h3 id=\"endpoints\"><a href=\"#endpoints\">​<span></span></a>Endpoints<span><span>New</span></span></h3>\n<p>The endpoint page is now tabbed, matching the catalog layout:</p>\n<ul role=\"list\">\n<li><strong>Overview</strong> — query plan diagram and metrics</li>\n<li><strong>Schema</strong> — the model attribute tree for the query’s return type</li>\n<li><strong>OpenAPI</strong> — a generated OpenAPI spec, for non-streaming queries</li>\n<li><strong>Source</strong> — the query source</li>\n</ul>\n<p>A new endpoint, <code>GET /api/schemas/queries/{queryName}/openapi</code>, generates the spec for any saved query by name.\nThe existing <code>/api/q/meta/{queryName}/oas</code> only covered HTTP-routable queries.</p>\n<p>Published query URLs shown in the UI now match where the endpoint is actually served — previously the raw\ndeclared URL was shown, which didn’t match the workspace-prefixed route.</p>\n<h3 id=\"local-environments\"><a href=\"#local-environments\">​<span></span></a>Local environments<span><span>Fixed</span></span></h3>\n<p>Three unhappy paths on the Local Environments page used to fail silently or mislead:</p>\n<ul role=\"list\">\n<li><strong>Compilation errors.</strong> A nebula script that failed to compile produced no feedback at all — the Nebula\nserver killed the websocket handler. Per-stack compilation errors are now accumulated and listed against\ntheir stack, clearing when a corrected version compiles.</li>\n<li><strong>Disconnected empty state.</strong> With nebula files defined but the server unreachable, the page claimed no\nenvironments were defined. It now shows <em>“Waiting to connect…”</em> with the names of the stacks it knows\nabout from the schema.</li>\n<li><strong>Component start failures.</strong> An invalid DDL or similar now shows its message in the tree tooltip, as a\nbanner on the component detail panel, and as a red Failed state in the status bar — which previously showed\n<em>“Starting…”</em> forever.</li>\n</ul>\n<p>Two connection bugs are fixed too. A dropped session (a network blip, or a non-text control frame) used to kill\nthe consumer coroutine permanently: the socket kept reconnecting and logging that it had connected, but nothing\nconsumed the new sessions, so schemas were never resubmitted. And rapid status transitions were being dropped —\nthe in-process HTTP component fires <em>Starting</em> and <em>Running</em> microseconds apart, and the sink was configured to\ndrop an update for every subscriber if any one of them was momentarily at zero demand, leaving consumers stuck\non “starting” forever.</p>\n<p>Projects that reference <code>NEBULA_*</code> environment variables no longer flash a red config-errors banner at startup\nwhile the local stack is still coming up. Orbital distinguishes “Nebula starting” from a real config failure\nand shows a quiet <em>“Starting local environment…”</em> instead, escalating to a proper error if the connection drops.</p>\n<h3 id=\"query-graph-debugger\"><a href=\"#query-graph-debugger\">​<span></span></a>Query graph debugger<span><span>New</span></span></h3>\n<p>The old Angular <code>graph-vis</code> tool is replaced by a React Flow rewrite under <code>tools/graph-debugger</code>. Paste the\ngraph the engine logs while planning a query and explore it interactively to work out why a query isn’t\nresolving.</p>\n<p>Large graphs used to lock the canvas, so it now starts empty: add a single type, expand outward from any node\nvia its off-canvas-neighbour badge, or drop the whole graph on at once for small ones. Auto-layout is\nleft-to-right hierarchical, matching the engine’s traversal direction, and dragging a node pins it.</p>\n<h3 id=\"smaller-ui-fixes\"><a href=\"#smaller-ui-fixes\">​<span></span></a>Smaller UI fixes<span><span>Fixed</span></span></h3>\n<ul role=\"list\">\n<li>Picking a result in the search bar navigated twice — once to the item you chose, and again to the first item\nin the list.</li>\n<li>The schema diagram’s markdown round-trip now serialises member keys as qualified names, so saved layouts\nresolve on re-render.</li>\n<li>Clicking a model node header in the schema diagram navigated to the literal path rather than the type’s\nqualified name.</li>\n<li>Operation parameter type names link through to the catalog, matching the return type’s behaviour.</li>\n<li>Query plans containing a Hazelcast node no longer throw.</li>\n<li>Loading buttons inside pop-ups are no longer obscured by incorrect styling.</li>\n<li>Alert action links (project errors, config errors, compilation errors) navigate to the correct workspace.</li>\n<li>A 404 for a type that doesn’t exist no longer bounces you out to the workspace selector — only a genuine\nworkspace-not-found does.</li>\n</ul>\n<div><span></span><span></span></div>\n<h2 id=\"other-fixes\"><a href=\"#other-fixes\">​<span></span></a>Other fixes<span><span>Fixed</span></span></h2>\n<ul role=\"list\">\n<li><strong>HTTP header parameters were matched by position, not name.</strong> <code>buildHttpHeaders</code> resolved each\n<code>@HttpHeader</code> parameter by its index in the supplied list, assuming that list matched the operation’s\ndeclared parameter order. It doesn’t — directly-provided parameters are appended after searched ones — so\nheader values could silently be attached to the wrong header.</li>\n<li><strong>A JDBC driver returning null threw an NPE</strong> rather than producing a null value.</li>\n<li><strong>Config loading could throw <code>ConcurrentModificationException</code></strong> because custom types were being registered\non every load rather than once at class initialisation.</li>\n<li><strong>An invalid PropelAuth API key returned 500.</strong> It now maps to 401 for a rejected token, 403 for a permitted\ntoken without access, and 503 when PropelAuth itself is unreachable.</li>\n<li><strong>A file watcher missed the contents of newly created directories.</strong> <code>java.nio.WatchService</code> doesn’t replay\nevents from before a key is registered, so writing <code>orbital/config/services.conf</code> in one go meant the watcher\nsaw only the <code>orbital/</code> directory and never the files inside it. Creating a directory now registers the watch\nand walks its existing contents.</li>\n<li><strong>Sankey lineage rows weren’t stamped with the query’s workspace</strong>, so the lineage view in the query profile\nwas empty for every workspace-scoped query.</li>\n<li><strong>History results for <code>PROVIDED</code> lineage nodes weren’t showing.</strong></li>\n<li><strong>Trace events from published-query routes</strong> were stamped with the default workspace rather than the one\nthat ran the query.</li>\n</ul>\n<div><span></span><span></span></div>\n<h2 id=\"in-case-you-missed-it\"><a href=\"#in-case-you-missed-it\">​<span></span></a>In case you missed it</h2>\n<p>Writing these notes turned up a gap in our own record-keeping. <strong>0.36.0</strong> (October 2025) and <strong>0.38.0-M1</strong>\n(June 2026) both shipped without a release announcement, and a handful of point releases went out quietly too.\nA fair amount of work reached you without us ever saying so.</p>\n<p>None of this is new in 0.39 — it’s all been in your hands for a while. It’s here because you may not know\nyou have it.</p>\n<h3 id=\"custom-kotlin-functions\"><a href=\"#custom-kotlin-functions\">​<span></span></a>Custom Kotlin functions<span><span>New</span></span></h3>\n<a href=\"https://hub.docker.com/r/orbitalhq/orbital/tags?name=0.36.0\" target=\"_blank\" rel=\"noreferrer\">Available since <!-- -->0.36.0</a>\n<p>You can extend Taxi’s standard library with your own functions, written in Kotlin and loaded at runtime. Declare\nthe function in Taxi, implement it in Kotlin, and it’s available in expressions, projections and queries like\nany built-in.</p>\n<p>This is the extension point to reach for when a transformation can’t be expressed in Taxi itself — a\nproprietary check-digit algorithm, a domain-specific parse, an internal encoding scheme.</p>\n<p>It’s documented at <a href=\"https://orbitalhq.com/docs/extending/custom-functions\">custom functions</a>; it has simply never appeared in a\nchangelog, because 0.36.0 never got one.</p>\n<p>It’s also the mechanism the <a href=\"#data-validation\">validation framework</a> will use when user-written validation\nrules land — the loader is already proven.</p>\n<h3 id=\"everything-else\"><a href=\"#everything-else\">​<span></span></a>Everything else</h3>\n<div><table><thead><tr><th>Feature</th><th>Shipped in</th><th>Documented</th></tr></thead><tbody><tr><td><a href=\"https://orbitalhq.com/docs/querying/scheduled-queries\">Scheduled queries</a> — run a query on a cron schedule</td><td>0.36.0</td><td>Yes</td></tr><tr><td><code>in</code> / <code>not in</code> operators, pushed down to SQL and MongoDB</td><td>0.36.0</td><td>Yes</td></tr><tr><td>MongoDB native aggregations via <code>@MongoAggregate</code></td><td>0.36.0</td><td>No</td></tr><tr><td><code>map { }</code> iterates a collection rather than operating on it whole</td><td>0.36.0</td><td>No</td></tr><tr><td>Negate (<code>!</code>) and modulo operators</td><td>0.36.0</td><td>No</td></tr><tr><td><code>env.conf</code> resolves environment variables inside annotations</td><td>0.36.0</td><td>Partly</td></tr><tr><td>Query-level Kafka offset override, by annotation</td><td>0.36.0</td><td>Yes</td></tr><tr><td>Custom error responses thrown from a <code>given { }</code> clause</td><td>0.36.0</td><td>No</td></tr><tr><td>Query error events persisted to the database</td><td>0.36.0</td><td>No</td></tr><tr><td>Roughly twenty stdlib functions — <code>append</code>, <code>average</code>, <code>joinToString</code>, <code>ifEmpty</code>, <code>orEmpty</code>, <code>size</code>, <code>isNullOrEmpty</code>, <code>indexOfItem</code>, <code>containsAll</code>, <code>containsAny</code>, <code>all</code>, <code>some</code>, <code>none</code>, <code>emptyInstance</code>, <code>startsWith</code>, <code>endsWith</code>, <code>matches</code>, <code>containsPattern</code>, <code>padStart</code>, <code>padEnd</code>, <code>applyFormat</code></td><td>0.36.0</td><td>No</td></tr><tr><td>Logbook HTTP request/response capture</td><td>0.36.0</td><td>No</td></tr><tr><td>File-system monitors filter irrelevant files, stopping continuous polling loops</td><td>0.36.7</td><td>n/a</td></tr><tr><td>WebSocket transport for schema publication</td><td>0.37.0-M4</td><td>Partly</td></tr><tr><td><a href=\"https://orbitalhq.com/docs/sdks/java-and-kotlin/spring-boot-schema-publisher\">Schema-publisher Spring Boot starter</a></td><td>0.37.0-M4</td><td>Yes</td></tr><tr><td>SOAP as a loadable language via <code>additionalSources</code></td><td>0.38.0-M1</td><td>Yes</td></tr><tr><td><a href=\"https://orbitalhq.com/docs/guides/working-with-xml\">XSD as a source format</a></td><td>0.38.0-M1</td><td>Yes</td></tr><tr><td><a href=\"https://orbitalhq.com/docs/describing-data-sources/databases#batching-writes\">JDBC batching</a> — <code>batchSize</code> / <code>batchDuration</code> on database writes</td><td>0.38.0-M1</td><td>Yes</td></tr><tr><td>Operations usable as callable expressions</td><td>0.38.0-M1</td><td>No</td></tr><tr><td><a href=\"https://orbitalhq.com/docs/describing-data-sources/authentication-to-services\">Multiple authentication tokens per service</a></td><td>0.38.0-M1</td><td>Yes</td></tr><tr><td>Environment-specific config files (<code>auth.conf</code> overridden per environment)</td><td>0.38.0-M1</td><td>Partly</td></tr><tr><td>New query-plan diagram</td><td>0.38.0-M1</td><td>No</td></tr><tr><td>Tabbed catalog layout, and README as the first tab in the project explorer</td><td>0.38.0-M1</td><td>n/a</td></tr><tr><td>Endpoint for submitting parameterized query objects</td><td>0.38.0-M1</td><td>No</td></tr><tr><td>JSON message body accepted on the SSE streaming endpoint</td><td>0.38.0-M1</td><td>No</td></tr><tr><td>Git configuration changes clean up stale clones</td><td>0.38.0-M1</td><td>n/a</td></tr><tr><td><code>schema-management</code> relicensed to Apache 2.0</td><td>0.38.0-M1</td><td>n/a</td></tr></tbody></table></div>\n<p>The undocumented rows are on our list. If one of them is something you’ve wanted, tell us and we’ll bump it.</p>\n<div><span></span><span></span></div>\n<h2 id=\"upgrading\"><a href=\"#upgrading\">​<span></span></a>Upgrading</h2>\n<p>This is a milestone build. Try it somewhere that isn’t production first.</p>\n<ol role=\"list\">\n<li>Read <a href=\"#breaking-changes\">Breaking changes</a> — particularly the license requirement and the\n<code>workspace-mode</code> toggle rename.</li>\n<li>Back up your query history database. The Flyway migration takes table locks to enforce the new non-null\ncolumns.</li>\n<li>If you use H2 as a data source, migrate off it before upgrading.</li>\n<li>If you rely on the standalone Jet pipeline server, stay on 0.38.x. Streaming queries are unaffected.</li>\n<li>Start Orbital and sign in. Your free license downloads automatically.</li>\n</ol>\n<p>Workspaces are opt-in. Leave <code>workspace-mode</code> at <code>None</code> and Orbital behaves as it always has, with a single\nworkspace and legacy routes intact — you can adopt workspaces when you’re ready.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2026-08-24-release-announcement-0.39.0\">0.39.1-M1 - Workspaces, Copilot, secrets and validation</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2026-08-24-release-announcement-0.39.0",
            "title": "0.39.1-M1 - Workspaces, Copilot, secrets and validation",
            "summary": "The first milestone of 0.39, the biggest change Orbital has had. Multiple workspaces per instance, organisations, a secrets manager with five backends, a data validation framework, an MCP server, and a new multi-file code editor.",
            "image": "https://orbitalhq.com/changelog-cards/2026-08-24-release-announcement-0.39.0.png",
            "date_modified": "2026-08-24T00:00:00.000Z",
            "date_published": "2026-08-24T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2026-07-16-jdbc-deletes-and-native-sql",
            "content_html": "<a href=\"https://hub.docker.com/r/orbitalhq/orbital/tags?name=0.38.0-M2\" target=\"_blank\" rel=\"noreferrer\">Available since <!-- -->0.38.0-M2</a>\n<p>Two additions to our database connectors in this release: deleting rows through TaxiQL, and running SQL\nyou’ve written yourself.</p>\n<h2 id=\"deleting-rows\"><a href=\"#deleting-rows\">​<span></span></a>Deleting rows</h2>\n<p>Databases connected to Orbital have long supported reads, inserts, updates and upserts. Deletes now join\nthe family - annotate a <code>write operation</code> with <code>@DeleteOperation</code>:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>@</span><span>DatabaseService</span><span>(</span><span>connection </span><span>=</span><span> </span><span>&quot;customers-database&quot;</span><span>)</span></span><span><span>service</span><span> </span><span>CustomerService</span><span> </span><span>{</span></span><span><span>   </span><span>@</span><span>DeleteOperation</span></span><span><span>   </span><span>write</span><span> </span><span>operation</span><span> </span><span>deleteCustomer</span><span>(</span><span>Customer</span><span>)</span><span>:</span><span>DeleteResult</span></span><span>\n</span><span><span>   </span><span>@</span><span>DeleteOperation</span></span><span><span>   </span><span>write</span><span> </span><span>operation</span><span> </span><span>deleteCustomers</span><span>(</span><span>Customer</span><span>[</span><span>]</span><span>)</span><span>:</span><span>DeleteResult</span></span><span><span>}</span></span></code></pre></div></div>\n<p>Rows are matched on the model’s <code>@Id</code> fields (composite keys work too - rows match on the combination),\nand you can pass a single instance or a whole array. Invoking it looks like any other mutation:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>given</span><span> </span><span>{</span></span><span><span>   customers</span><span> </span><span>:</span><span> Customer</span><span>[</span><span>]</span><span> </span><span>=</span><span> </span><span>[</span></span><span><span>      </span><span>{</span><span> id </span><span>:</span><span> </span><span>123</span><span> </span><span>}</span><span>,</span></span><span><span>      </span><span>{</span><span> id </span><span>:</span><span> </span><span>456</span><span> </span><span>}</span></span><span><span>   </span><span>]</span></span><span><span>}</span></span><span><span>call CustomerService</span><span>:</span><span>:</span><span>deleteCustomers</span></span></code></pre></div></div>\n<p>You get back a <code>deletedCount</code> telling you how many rows went away. Deletes run in a single transaction,\nso a failure partway through rolls the whole thing back rather than leaving you half-deleted.</p>\n<h2 id=\"native-sql-queries\"><a href=\"#native-sql-queries\">​<span></span></a>Native SQL queries</h2>\n<p>Orbital’s <code>table</code> operations cover most day-to-day querying, and keep your queries portable across\ndatabases. But sometimes you just want to write the SQL yourself - a join, an aggregation, or something\ndialect-specific. The new <code>@SqlQuery</code> annotation is that escape hatch:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>@</span><span>DatabaseService</span><span>(</span><span>connection </span><span>=</span><span> </span><span>&quot;films-database&quot;</span><span>)</span></span><span><span>service</span><span> </span><span>FilmStats</span><span> </span><span>{</span></span><span><span>   </span><span>@</span><span>SqlQuery</span><span>(</span><span>sql </span><span>=</span><span> </span><span>&quot;&quot;</span><span>&quot;</span></span><span><span>      SELECT d</span><span>.</span><span>name AS director</span><span>,</span><span> </span><span>COUNT</span><span>(</span><span>f</span><span>.</span><span>id</span><span>)</span><span> AS filmCount</span></span><span><span>      FROM film f</span></span><span><span>      JOIN director d ON f</span><span>.</span><span>director_id </span><span>=</span><span> d</span><span>.</span><span>id</span></span><span><span>      GROUP BY d</span><span>.</span><span>name</span></span><span><span>   </span><span>&quot;&quot;</span><span>&quot;</span><span>)</span></span><span><span>   </span><span>operation</span><span> </span><span>filmsPerDirector</span><span>(</span><span>)</span><span> </span><span>:</span><span> DirectorFilmSummary</span><span>[</span><span>]</span></span><span><span>}</span></span></code></pre></div></div>\n<p>The SQL is passed to your database exactly as written, and result columns map back to your Taxi model’s\nfields by name or alias. One annotation covers both directions: a plain <code>operation</code> runs the SQL as a\nquery, while a <code>write operation</code> runs it as a statement (<code>UPDATE</code>, <code>DELETE</code>, <code>INSERT</code>) and returns the\nnumber of rows affected.</p>\n<p>Operation parameters bind into the SQL with the <code>:parameterName</code> syntax:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>@</span><span>SqlQuery</span><span>(</span><span>sql </span><span>=</span><span> </span><span>&quot;SELECT title, release_year AS releaseYear FROM film WHERE release_year &gt; :year&quot;</span><span>)</span></span><span><span>operation</span><span> </span><span>recentFilms</span><span>(</span><span>year</span><span> </span><span>:</span><span> ReleaseYear</span><span>)</span><span> </span><span>:</span><span> Film</span><span>[</span><span>]</span></span></code></pre></div></div>\n<p>Values are always bound as prepared-statement parameters - never spliced into the SQL text - so\nuser-supplied values can’t alter your query.</p>\n<p>Both features work across all of Orbital’s SQL database connectors. Full details are in the\n<a href=\"https://orbitalhq.com/docs/describing-data-sources/databases#deleting-data-from-a-database\">database docs</a>, including\n<a href=\"https://orbitalhq.com/docs/describing-data-sources/databases#running-native-sql-queries\">native query behaviour notes</a>.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2026-07-16-jdbc-deletes-and-native-sql\">Database connectors: delete support and native SQL queries</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2026-07-16-jdbc-deletes-and-native-sql",
            "title": "Database connectors: delete support and native SQL queries",
            "summary": "Delete rows through TaxiQL with @DeleteOperation, and run hand-written SQL - joins, aggregations, dialect-specific statements - with @SqlQuery.",
            "image": "https://orbitalhq.com/changelog-cards/2026-07-16-jdbc-deletes-and-native-sql.png",
            "date_modified": "2026-07-16T00:00:00.000Z",
            "date_published": "2026-07-16T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2026-07-04-collection-options",
            "content_html": "<a href=\"https://hub.docker.com/r/orbitalhq/orbital/tags?name=0.38.0-M2\" target=\"_blank\" rel=\"noreferrer\">Available since <!-- -->0.38.0-M2</a>\n<p>You can now shape the results of a query directly in TaxiQL - limiting how many rows come back, paginating through them,\nsorting them, and removing duplicates. We’re calling these <strong>collection options</strong>, and they look like this:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> Person</span><span>[</span><span>]</span><span>(</span><span> CountryCode </span><span>==</span><span> </span><span>&quot;GB&quot;</span><span>,</span><span> orderBy</span><span>:</span><span> DateOfBirth</span><span> desc</span><span>,</span><span> offset</span><span>:</span><span> </span><span>20</span><span>,</span><span> limit</span><span>:</span><span> </span><span>10</span><span> </span><span>)</span><span> </span><span>}</span></span></code></pre></div></div>\n<p>These are the standard database-style controls we all already know and love - <code>limit</code>, <code>offset</code>, <code>orderBy</code> and\n<code>uniqueBy</code> - but they work on <strong>any</strong> query, against any source.</p>\n<p>When the source can apply an option itself, Orbital pushes the work down to it. When it can’t, Orbital does the work after fetching. Either way you get the same result.</p>\n<h2 id=\"a-few-examples\"><a href=\"#a-few-examples\">​<span></span></a>A few examples</h2>\n<p>Sort and take the top N:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> Film</span><span>[</span><span>]</span><span>(</span><span> orderBy</span><span>:</span><span> RottenTomatoesScore</span><span> desc</span><span>,</span><span> limit</span><span>:</span><span> </span><span>10</span><span> </span><span>)</span><span> </span><span>}</span></span></code></pre></div></div>\n<p>Sort by more than one field, and paginate:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> Person</span><span>[</span><span>]</span><span>(</span><span> orderBy</span><span>:</span><span> </span><span>[</span><span>CountryCode asc</span><span>,</span><span> DateOfBirth desc</span><span>]</span><span>,</span><span> offset</span><span>:</span><span> </span><span>40</span><span>,</span><span> limit</span><span>:</span><span> </span><span>20</span><span> </span><span>)</span><span> </span><span>}</span></span></code></pre></div></div>\n<p>Options work on projections too, including nested collections - handy for trimming child records per parent:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> Customer</span><span>[</span><span>]</span><span> </span><span>}</span><span> </span><span>as</span><span> </span><span>{</span></span><span><span>  name</span><span> </span><span>:</span><span> CustomerName</span></span><span><span>  </span><span>// Only the 20 most recent transactions per customer</span></span><span><span>  transactions</span><span> </span><span>:</span><span> Transaction</span><span>[</span><span>]</span><span>(</span><span> limit</span><span>:</span><span> </span><span>20</span><span>,</span><span> orderBy</span><span>:</span><span> TransactionDate</span><span> desc </span><span>)</span></span><span><span>}</span><span>[</span><span>]</span></span></code></pre></div></div>\n<p>Values can be literals or query parameters, so <code>limit: maxRows</code> works just as well when <code>maxRows</code> is a query argument.</p>\n<h2 id=\"how-pushdown-works\"><a href=\"#how-pushdown-works\">​<span></span></a>How pushdown works</h2>\n<p>For each option, Orbital’s query planner decides - per data source - whether to push it down or to apply it itself.</p>\n<p>Pushing down tot he source system is only permitted if it doesn’t change the answer: if a source can’t sort, Orbital won’t push a\n<code>limit</code> below the (missing) sort either, because limiting before sorting would hand back the wrong rows.</p>\n<p>Here’s what’s supported in this release:</p>\n<ul role=\"list\">\n<li><strong>SQL databases</strong> (Postgres, MySQL, MSSQL, Oracle, Redshift, Snowflake) push <code>limit</code>, <code>offset</code> and <code>orderBy</code> straight\ninto the generated SQL, in the right dialect for your driver.</li>\n<li><strong>MongoDB</strong> pushes <code>limit</code>, <code>offset</code> and <code>orderBy</code> into the Mongo query as <code>sort</code> / <code>skip</code> / <code>limit</code>.</li>\n<li><strong>Hazelcast</strong> pushes <code>limit</code> down as a key-ordered page; everything else is applied by Orbital.</li>\n<li><strong>Everything else</strong> - REST APIs, Kafka, and any other source - is applied by Orbital after fetching. No source-side\nsupport required.</li>\n</ul>\n<p><code>limit</code> also works on streaming queries, completing the stream once it has emitted N items.</p>\n<h2 id=\"coming-next\"><a href=\"#coming-next\">​<span></span></a>Coming next</h2>\n<p><code>after</code> / <code>before</code> cursors are reserved for cursor-based pagination and will land in a future release - for now they\nparse but are rejected at runtime.</p>\n<p>Full details are in the <a href=\"https://orbitalhq.com/docs/querying/collection-options\">collection options docs</a>, and the syntax reference lives in\nthe <a href=\"https://taxilang.org/docs/taxiql/querying#collection-options\" target=\"_blank\" rel=\"noreferrer\">Taxi language docs</a>.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2026-07-04-collection-options\">Collection options: limit, sort and paginate any query</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2026-07-04-collection-options",
            "title": "Collection options: limit, sort and paginate any query",
            "summary": "TaxiQL now supports collection options - limit, offset, orderBy and uniqueBy - pushed down to your data sources where they can be, and applied by Orbital everywhere else.",
            "image": "https://orbitalhq.com/changelog-cards/2026-07-04-collection-options.png",
            "date_modified": "2026-07-04T00:00:00.000Z",
            "date_published": "2026-07-04T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2026-05-08-release-announcement-0.38.0",
            "content_html": "<h2 id=\"new-features\"><a href=\"#new-features\">​<span></span></a>New Features</h2>\n<h3 id=\"anonymous-access-for-published-queries\"><a href=\"#anonymous-access-for-published-queries\">​<span></span></a>Anonymous access for published queries</h3>\n<a href=\"https://hub.docker.com/r/orbitalhq/orbital/tags?name=0.38.0\" target=\"_blank\" rel=\"noreferrer\">Available since <!-- -->0.38.0</a>\n<p>Queries <a href=\"https://orbitalhq.com/docs/querying/queries-as-endpoints\">published as HTTP endpoints</a> can now opt out of authentication on a per-query basis using the new <code>@com.orbitalhq.authentication.AllowAnonymous</code> annotation.</p>\n<p>When Orbital is deployed with an authentication provider configured, all published query endpoints require a valid token by default. <code>@AllowAnonymous</code> relaxes that requirement for a single query — useful for health checks, public catalogues, or any endpoint intended to be reachable without a token. Other queries in the same workspace are unaffected.</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>import</span><span> taxi</span><span>.</span><span>http</span><span>.</span><span>HttpOperation</span></span><span><span>import</span><span> com</span><span>.</span><span>orbitalhq</span><span>.</span><span>authentication</span><span>.</span><span>AllowAnonymous</span></span><span>\n</span><span><span>@</span><span>HttpOperation</span><span>(</span><span>url </span><span>=</span><span> </span><span>&#x27;/api/q/healthcheck&#x27;</span><span>,</span><span> method </span><span>=</span><span> </span><span>&#x27;GET&#x27;</span><span>)</span></span><span><span>@</span><span>AllowAnonymous</span></span><span><span>query</span><span> </span><span>Healthcheck</span><span> </span><span>{</span></span><span><span>  </span><span>find</span><span> </span><span>{</span><span> status</span><span>:</span><span> String</span><span> </span><span>=</span><span> </span><span>&quot;ok&quot;</span><span> </span><span>}</span></span><span><span>}</span></span></code></pre></div></div>\n<p>The annotation only affects the authentication check. Data policies and authorization rules still run.</p>\n<p>See the <a href=\"https://orbitalhq.com/docs/querying/queries-as-endpoints#allowing-anonymous-access\">docs on allowing anonymous access</a> for more detail.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2026-05-08-release-announcement-0.38.0\">0.38.0 - Anonymous access for published queries</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2026-05-08-release-announcement-0.38.0",
            "title": "0.38.0 - Anonymous access for published queries",
            "summary": "Orbital 0.38.0 - Per-query opt-out from authentication for published HTTP query endpoints",
            "image": "https://orbitalhq.com/changelog-cards/2026-05-08-release-announcement-0.38.0.png",
            "date_modified": "2026-05-08T00:00:00.000Z",
            "date_published": "2026-05-08T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2026-03-24-query-api-content-type-breaking-change",
            "content_html": "<a href=\"https://hub.docker.com/r/orbitalhq/orbital/tags?name=0.38.x\" target=\"_blank\" rel=\"noreferrer\">Available since <!-- -->0.38.x</a>\n<p>As part of upcoming feature work for improved SDK tooling for JVM and TS/JS ecosystems, there are two\nbreaking changes to the query API are shipping in 0.38.x (and nightly builds of <code>next</code>). Neither affects 0.37.x.</p>\n<h2 id=\"breaking-apitaxiql-no-longer-accepts-applicationjson\"><a href=\"#breaking-apitaxiql-no-longer-accepts-applicationjson\">​<span></span></a>Breaking: <code>/api/taxiql</code> no longer accepts <code>application/json</code></h2>\n<p>The <code>/api/taxiql</code> streaming query endpoint previously accepted requests with <code>Content-Type: application/json</code>.\nThis is no longer supported.</p>\n<p>Use either of the following content types instead:</p>\n<ul role=\"list\">\n<li><code>application/taxiql</code> (Preferred, but less standard)</li>\n<li><code>text/plain</code> (Standard, but less accurate)</li>\n</ul>\n<p>Either are fine to use, and both content types are already supported in all versions of Orbital today, so you can make this change now, without waiting.</p>\n<p>If you’re sending queries via <code>curl</code> or an HTTP client, update accordingly:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span># Before (no longer works)</span></span><span><span>curl</span><span> </span><span>-X</span><span> POST /api/taxiql </span><span>\\</span></span><span><span>  </span><span>-H</span><span> </span><span>&quot;Content-Type: application/json&quot;</span><span> </span><span>\\</span></span><span><span>  </span><span>-d</span><span> </span><span>&#x27;{ &quot;query&quot;: &quot;find { ... }&quot; }&#x27;</span></span><span>\n</span><span><span># After</span></span><span><span>curl</span><span> </span><span>-X</span><span> POST /api/taxiql </span><span>\\</span></span><span><span>  </span><span>-H</span><span> </span><span>&quot;Content-Type: application/taxiql&quot;</span><span> </span><span>\\</span></span><span><span>  --data-raw </span><span>&#x27;find { ... }&#x27;</span></span></code></pre></div></div>\n<h2 id=\"removal-of-apivyneql\"><a href=\"#removal-of-apivyneql\">​<span></span></a>Removal of <code>/api/vyneql</code></h2>\n<p>The legacy <code>/api/vyneql</code> endpoint has been removed. It was a holdover from the previous product name and has been\nsuperseded by <code>/api/taxiql</code> for some time. If you were still referencing <code>/api/vyneql</code>, update your calls to use <code>/api/taxiql</code>.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2026-03-24-query-api-content-type-breaking-change\">Breaking change: /api/taxiql content-type and removal of /api/vyneql</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2026-03-24-query-api-content-type-breaking-change",
            "title": "Breaking change: /api/taxiql content-type and removal of /api/vyneql",
            "summary": "The /api/taxiql endpoint no longer accepts application/json. The legacy /api/vyneql endpoint has been removed.",
            "image": "https://orbitalhq.com/changelog-cards/2026-03-24-query-api-content-type-breaking-change.png",
            "date_modified": "2026-03-24T00:00:00.000Z",
            "date_published": "2026-03-24T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2026-03-23-release-announcement-0.37.0-M3",
            "content_html": "<p>Orbital 0.37.0-M3 is the third milestone release of 0.37.\nWe haven’t published release notes on <code>0.37.0-M1</code> or <code>-M2</code>, we’ll cover off those releases too.</p>\n<p>The headline features are a new <code>parseJson()</code> standard library function, the ability to export query history\nas <a href=\"https://preflight.orbitalhq.com\" target=\"_blank\" rel=\"noreferrer\">Preflight</a> test specs, and a significant improvement to observability — with request\nparameters now captured on every remote call across all connectors.</p>\n<p>There’s also a crop of reliability improvements: better git sync resilience, a fix for a stack overflow in\nthe query engine, and an internal overhaul that removes a class of thread-pool starvation under concurrent load.</p>\n<p>As a milestone release, 0.37.0-M3 is intended for early adopters and teams that want to test ahead of the\nGA release. Feedback welcome.</p>\n<h2 id=\"new-features\"><a href=\"#new-features\">​<span></span></a>New Features</h2>\n<h3 id=\"parsejson-stdlib-function\"><a href=\"#parsejson-stdlib-function\">​<span></span></a><code>parseJson()</code> stdlib function</h3>\n<a href=\"https://hub.docker.com/r/orbitalhq/orbital/tags?name=0.37.0-M3\" target=\"_blank\" rel=\"noreferrer\">Available since <!-- -->0.37.0-M3</a>\n<p>You can now use <code>parseJson()</code> in your queries and projections to parse a JSON string into a typed value.</p>\n<p>This is useful when JSON arrives embedded in a field as a string — a common pattern in event-driven systems where a\nmessage envelope wraps a serialized payload.</p>\n<section><h3>Using parseJson()</h3><div><div><span>Show schema</span><div><a href=\"https://playground.taxilang.org\" target=\"_blank\" rel=\"noreferrer\"><span>Edit this on Taxi Playground</span></a><span>Run this query</span></div></div><div><div><textarea id=\"_R_16qqj5_-query\" rows=\"10\" wrap=\"off\" spellCheck=\"false\">given {\n   customer: Customer = {\n      name: &#x27;Alice&#x27;,\n      addressJson: &#x27;{ &quot;city&quot;: &quot;London&quot;, &quot;country&quot;: &quot;UK&quot; }&#x27;\n   }\n}\nfind {\n   name: Name\n   address: Address = parseJson(customer.addressJson, Address)\n}</textarea></div></div></div></section>\n<h3 id=\"preflight-test-spec-export\"><a href=\"#preflight-test-spec-export\">​<span></span></a>Preflight test spec export</h3>\n<a href=\"https://hub.docker.com/r/orbitalhq/orbital/tags?name=0.37.0-M2\" target=\"_blank\" rel=\"noreferrer\">Available since <!-- -->0.37.0-M2</a>\n<p>Query history can now be exported as a <a href=\"https://preflight.orbitalhq.com\" target=\"_blank\" rel=\"noreferrer\">Preflight</a> test spec, in addition to the\nexisting ZIP regression pack format.</p>\n<p>When downloading a regression pack from query history, you can now choose between <code>Zip</code> (the original format)\nand <code>Preflight</code> spec files.</p>\n<p><a href=\"https://preflight.orbitalhq.com/spec-files\" target=\"_blank\" rel=\"noreferrer\">Preflight Spec Files</a> are markdown files that describe a test scenario,\nalong with all the information to stub out query services:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>---</span></span><span><span>spec-version: 0.1</span></span><span><span>---</span></span><span><span> </span></span><span><span># Test name</span></span><span><span> </span></span><span><span>Optional description paragraph.</span></span><span><span> </span></span><span><span>## Query</span></span><span><span> </span></span><span><span>```taxiql</span></span><span><span>find { Customer( CustomerId == &quot;C-100&quot; ) } as CustomerProfile</span></span><span><span>```</span></span><span><span> </span></span><span><span>## Data Sources</span></span><span><span> </span></span><span><span>### Customer API</span></span><span><span>&lt;!-- operation: getCustomer --&gt;</span></span><span><span> </span></span><span><span>Response:</span></span><span><span>```json</span></span><span><span>{ &quot;id&quot;: &quot;C-100&quot;, &quot;name&quot;: &quot;Alice Smith&quot;, &quot;age&quot;: 30 }</span></span><span><span>```</span></span><span><span> </span></span><span><span>## Expected Result</span></span><span><span> </span></span><span><span>```json</span></span><span><span>{ &quot;id&quot;: &quot;C-100&quot;, &quot;name&quot;: &quot;Alice Smith&quot;, &quot;age&quot;: 30, &quot;isAdult&quot;: true }</span></span><span><span>```</span></span></code></pre></div></div>\n<p>By capturing these using the data from your queries, you can very quickly build test suites for\nyour taxi projects.</p>\n<p>Preflight tests are runnable using <a href=\"https://preflight.orbitalhq.com/\" target=\"_blank\" rel=\"noreferrer\">Gradle</a>, so works great with established build tooling and CI/CD.</p>\n<h2 id=\"observability\"><a href=\"#observability\">​<span></span></a>Observability</h2>\n<h3 id=\"request-parameters-captured-on-trace-events\"><a href=\"#request-parameters-captured-on-trace-events\">​<span></span></a>Request parameters captured on trace events</h3>\n<p>The capture of input parameters for events shown in the Trace View has been extended, capturing inputs across all connectors.</p>\n<h3 id=\"jfr-flight-recorder\"><a href=\"#jfr-flight-recorder\">​<span></span></a>JFR flight recorder</h3>\n<a href=\"https://hub.docker.com/r/orbitalhq/orbital/tags?name=0.37.0-M1\" target=\"_blank\" rel=\"noreferrer\">Available since <!-- -->0.37.0-M1</a>\n<p>Orbital now supports capturing Java Flight Recorder traces for capturing diagnostic data.\nThis feature is disabled by default, but can be enabled <a href=\"https://orbitalhq.com/docs/deploying/capturing-diagnostics#enabling-flight-recorder\">through configuration</a></p>\n<p><code>0.37.0-M3</code> extends this to further capture exception data.</p>\n<h2 id=\"reliability--performance\"><a href=\"#reliability--performance\">​<span></span></a>Reliability &amp; Performance</h2>\n<h3 id=\"query-engine-fix-for-circular-operation-types-orb-1081\"><a href=\"#query-engine-fix-for-circular-operation-types-orb-1081\">​<span></span></a>Query engine: fix for circular operation types (ORB-1081)</h3>\n<a href=\"https://hub.docker.com/r/orbitalhq/orbital/tags?name=0.37.0-M2\" target=\"_blank\" rel=\"noreferrer\">Available since <!-- -->0.37.0-M2</a>\n<p>Fixed a bug where an operation whose return type also appeared as an input parameter type would trigger\nrecursive parameter construction, ultimately causing a stack overflow. Type-level cycle detection is now\napplied during object building, so these circular type graphs are handled cleanly.</p>\n<h3 id=\"git-sync-failures-are-no-longer-fatal\"><a href=\"#git-sync-failures-are-no-longer-fatal\">​<span></span></a>Git sync failures are no longer fatal</h3>\n<a href=\"https://hub.docker.com/r/orbitalhq/orbital/tags?name=0.37.0-M3\" target=\"_blank\" rel=\"noreferrer\">Available since <!-- -->0.37.0-M3</a>\n<p>A git repository sync failure at startup is now treated as a warning rather than an error. If a\nlocally-cached copy of the schema is available, Orbital will use it and continue running — reporting a\n<code>WARNING</code> state in the UI rather than refusing to start.</p>\n<p>The UI also now distinguishes between <code>ERROR</code> (schema could not be loaded at all) and <code>WARNING</code> (schema\nloaded from local cache), with separate alerts and colour-coded severity badges, so it’s clearer what’s\nactually happening.</p>\n<p>A rate limiter (via resilience4j) has also been introduced on git repository polling, to prevent remote\ngit servers from denying requests when Orbital is polling frequently.</p>\n<h3 id=\"eliminated-blocking-calls-from-the-query-engine\"><a href=\"#eliminated-blocking-calls-from-the-query-engine\">​<span></span></a>Eliminated blocking calls from the query engine</h3>\n<a href=\"https://hub.docker.com/r/orbitalhq/orbital/tags?name=0.37.0-M1\" target=\"_blank\" rel=\"noreferrer\">Available since <!-- -->0.37.0-M1</a>\n<p>The internal TaxiQL query engine has been refactored to remove blocking calls. Under specific conditions, it was\npreviously possible for the query engine to get into thread starvation, waiting for I/O calls to complete.</p>\n<p>By migrating the core query engine away from blocking, this removes the risk</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2026-03-23-release-announcement-0.37.0-M3\">0.37.0-M3 - Preflight exports, parseJson, and observability improvements</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2026-03-23-release-announcement-0.37.0-M3",
            "title": "0.37.0-M3 - Preflight exports, parseJson, and observability improvements",
            "summary": "Orbital 0.37.0-M3 - Preflight test spec exports, parseJson stdlib function, improved query observability, and performance improvements",
            "image": "https://orbitalhq.com/changelog-cards/2026-03-23-release-announcement-0.37.0-M3.png",
            "date_modified": "2026-03-23T00:00:00.000Z",
            "date_published": "2026-03-23T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2026-02-16-release-announcement-0.36.6",
            "content_html": "<p>Orbital 0.36.6 introduces enhanced authentication capabilities for complex service integration scenarios.</p>\n<h2 id=\"new-features\"><a href=\"#new-features\">​<span></span></a>New Features</h2>\n<ul role=\"list\">\n<li>Added support for multiple authentication tokens per service (<a href=\"https://linear.app/orbital/issue/ORB-1065\" target=\"_blank\" rel=\"noreferrer\">ORB-1065</a>)<!-- -->\n<ul role=\"list\">\n<li>Services can now use multiple authentication mechanisms simultaneously (e.g., API key header + query param)</li>\n<li>Define an array of auth schemes instead of a single object in <code>auth.conf</code></li>\n<li>Fully backwards compatible with existing single-scheme configurations</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"example\"><a href=\"#example\">​<span></span></a>Example</h3>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>authenticationTokens {</span></span><span><span>  &quot;com.foo.service.*&quot; [</span></span><span><span>    { type: HttpHeader, value: ${API_KEY}, headerName: X-API-KEY },</span></span><span><span>    { type: QueryParam, parameterName: apiKey, value: ${API_SECRET} }</span></span><span><span>  ]</span></span><span><span>}</span></span></code></pre></div></div>\n<p>There are no other functional changes in this release.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2026-02-16-release-announcement-0.36.6\">0.36.6 released</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2026-02-16-release-announcement-0.36.6",
            "title": "0.36.6 released",
            "summary": "Orbital 0.36.6 introduces enhanced authentication capabilities for complex service integration scenarios.",
            "image": "https://orbitalhq.com/changelog-cards/2026-02-16-release-announcement-0.36.6.png",
            "date_modified": "2026-02-16T00:00:00.000Z",
            "date_published": "2026-02-16T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2026-02-13-release-announcement-0.36.5",
            "content_html": "<p>Orbital 0.36.5 is a patch release that updates the Taxi compiler dependency to fix enum-related compilation errors.</p>\n<h2 id=\"bug-fixes\"><a href=\"#bug-fixes\">​<span></span></a>Bug Fixes</h2>\n<ul role=\"list\">\n<li>Updated Taxi version to 1.69.1 to fix incorrect “Enum does not contain a member of” compilation errors (<a href=\"https://linear.app/orbital/issue/ORB-1064\" target=\"_blank\" rel=\"noreferrer\">ORB-1064</a>)<!-- -->\n<ul role=\"list\">\n<li>Fixed poor state management and hashcode handling when enum synonyms (extensions) are used</li>\n<li>This resolves false positive errors that could occur when working with enum types that have synonyms defined</li>\n</ul>\n</li>\n</ul>\n<p>There are no other functional changes in this release.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2026-02-13-release-announcement-0.36.5\">0.36.5 released</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2026-02-13-release-announcement-0.36.5",
            "title": "0.36.5 released",
            "summary": "Orbital 0.36.5 is a patch release that updates the Taxi compiler dependency to fix enum-related compilation errors.",
            "image": "https://orbitalhq.com/changelog-cards/2026-02-13-release-announcement-0.36.5.png",
            "date_modified": "2026-02-13T00:00:00.000Z",
            "date_published": "2026-02-13T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2026-01-08-release-announcement-0.36.4",
            "content_html": "<p>Orbital 0.36.4 introduces a new debugging feature for advanced troubleshooting.</p>\n<h2 id=\"new-features\"><a href=\"#new-features\">​<span></span></a>New Features</h2>\n<ul role=\"list\">\n<li>Added a flight recorder endpoint for debugging purposes (disabled by default)<!-- -->\n<ul role=\"list\">\n<li>Provides JVM Flight Recorder data for performance analysis and troubleshooting</li>\n<li>Can be enabled via configuration when needed for debugging</li>\n</ul>\n</li>\n</ul>\n<p>There are no other functional changes in this release.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2026-01-08-release-announcement-0.36.4\">0.36.4 released</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2026-01-08-release-announcement-0.36.4",
            "title": "0.36.4 released",
            "summary": "Orbital 0.36.4 introduces a new debugging feature for advanced troubleshooting.",
            "image": "https://orbitalhq.com/changelog-cards/2026-01-08-release-announcement-0.36.4.png",
            "date_modified": "2026-01-08T00:00:00.000Z",
            "date_published": "2026-01-08T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2025-12-22-release-announcement-0.36.3",
            "content_html": "<p>Orbital 0.36.3 is a patch release that addresses issues with very large lineage records.</p>\n<h2 id=\"bug-fixes\"><a href=\"#bug-fixes\">​<span></span></a>Bug Fixes</h2>\n<ul role=\"list\">\n<li>Fixed buffer overflow when writing very large lineage records (<a href=\"https://linear.app/orbital/issue/ORB-1054\" target=\"_blank\" rel=\"noreferrer\">ORB-1054</a>)</li>\n<li>Increased maximum message size from 16MB to 64MB</li>\n<li>Added warning logging when messages are too large</li>\n</ul>\n<p>There are no other functional changes in this release.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2025-12-22-release-announcement-0.36.3\">0.36.3 released</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2025-12-22-release-announcement-0.36.3",
            "title": "0.36.3 released",
            "summary": "Orbital 0.36.3 is a patch release that addresses issues with very large lineage records.",
            "image": "https://orbitalhq.com/changelog-cards/2025-12-22-release-announcement-0.36.3.png",
            "date_modified": "2025-12-22T00:00:00.000Z",
            "date_published": "2025-12-22T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2025-12-05-changes-to-start-and-default-config",
            "content_html": "<p>We’ve made some small changes on how to get Orbital running. We’ve also added a <a href=\"#what-do-i-need-to-do\">new config variable</a> to centralize\nwhere Orbital stores all it’s internal files. <em>You should add this config variable to your own deployments before 0.37 is released</em>.</p>\n<h2 id=\"new-way-to-start-orbital---startsh\"><a href=\"#new-way-to-start-orbital---startsh\">​<span></span></a>New way to start Orbital - start.sh</h2>\n<p>We’ve updated the way people get started on Orbital - replacing our docker-compose.yml with a bash script.</p>\n<p>If you head over to <a href=\"https://orbitalhq.com\" target=\"_blank\" rel=\"noreferrer\">orbitalhq.com</a>, you’ll notice that we’re now asking people to start\nby curling:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>curl</span><span> </span><span>-sSL</span><span> https://start.orbitalhq.com/start.sh </span><span>|</span><span> </span><span>bash</span></span></code></pre></div></div>\n<p>You can <a href=\"https://start.orbitalhq.com/start.sh\" target=\"_blank\" rel=\"noreferrer\">view the bash script</a> - it’s still running the same <a href=\"https://start.orbitalhq.com/docker-compose.yml\" target=\"_blank\" rel=\"noreferrer\">docker-compose.yml</a>,\nbut we’ve just added some scripting to make the experience a bit better.</p>\n<h2 id=\"what-do-i-need-to-do\"><a href=\"#what-do-i-need-to-do\">​<span></span></a>What do I need to do?</h2>\n<p>There’s very little that’s changed.</p>\n<p>You should add <code>--vyne.app.data.path=/a/path/for/your/config</code> to your config. It’s safe to do this now, before 0.37.0 is released.</p>\n<p>It’s also safe not to do this at all, and Orbital will work with reasonable defaults. But, our new preferred config includes setting this property.</p>\n<h2 id=\"what-we-changed\"><a href=\"#what-we-changed\">​<span></span></a>What we changed:</h2>\n<ul role=\"list\">\n<li>By default, the app now launches with user permissions, rather than root</li>\n<li>All app data (license files, temporary write queues, lucene search indices) are now written to the same, configurable directory (<code>--vyne.app.data.path</code>)</li>\n<li>Docker Volume directories are created before docker starts</li>\n</ul>\n<h2 id=\"why-we-did-this---the-pains-of-docker-compose\"><a href=\"#why-we-did-this---the-pains-of-docker-compose\">​<span></span></a>Why we did this - the pains of Docker Compose</h2>\n<p>We still ❤️ Docker Compose - it’s our preferred way for people to start the stack. But, we hit issues that made the experience unpleasant for developers…</p>\n<h3 id=\"file-permissions\"><a href=\"#file-permissions\">​<span></span></a>File permissions</h3>\n<p>When you’re working with Orbital, we expect you’ll do some stuff inside the browser, and other stuff inside your IDE.</p>\n<p>However, if Docker creates the directories you’re using - <strong>even if your docker container is running with a non-root user</strong>, then you can end up\nwith file permissions errors. You - the user - lack the permissions to edit the files that get created.</p>\n<p>The <code>start.sh</code> fixes this by simply creating the directories before docker launches,</p>\n<h3 id=\"user-permissions\"><a href=\"#user-permissions\">​<span></span></a>User permissions</h3>\n<p>Telling Docker Compose to launch in a non-root user is slightly trickier than you’d expect. It’s not <em>hard</em>, but you can’t\nsay “run with my account”, you have to say “run with <em>this</em> account”.</p>\n<p>The idiomatic way to address this is:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>services</span><span>:</span></span><span><span>    </span></span><span><span>   </span><span>orbital</span><span>:</span></span><span><span>      </span><span>image</span><span>:</span><span> orbitalhq/orbital</span><span>:</span><span>$</span><span>{</span><span>ORBITAL_VERSION</span><span>:</span><span>-</span><span>next</span><span>}</span></span><span><span>      </span><span>user</span><span>:</span><span> </span><span>&quot;${UID}:${GID}&quot;</span></span></code></pre></div></div>\n<p>And then have a <code>.env</code> file that defines those variables:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>UID=1000</span></span><span><span>GID=1000</span></span></code></pre></div></div>\n<p>This isn’t difficult, but it’s awkward for a user to just get started. So, the script handles this for you.</p>\n<h3 id=\"networking\"><a href=\"#networking\">​<span></span></a>Networking</h3>\n<p>Orbital is an integration platform, so when we ship demos or test stacks, we need things to integrate.</p>\n<p>We solved that by building <a href=\"https://nebula.orbitalhq.com\" target=\"_blank\" rel=\"noreferrer\">Nebula</a> to deploy local environments. However,\nNebula deploys other docker containers - and docker-launching-docker varies between OS’s. That meant our docker-compose.yml\nis OS-specific. We built tools to detect and try to serve the correct docker-compose.yml, but it’s clumsy if you’re running from the command line.</p>\n<p>Our script detects which OS you’re running on, and downloads the correct docker-compose file for you.</p>\n<h2 id=\"summary\"><a href=\"#summary\">​<span></span></a>Summary</h2>\n<p>That it! The new launcher script is live now, and config variables are shipping now in <code>next</code> and will be part of the <code>0.37</code> release.</p>\n<p>Happy integrating.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2025-12-05-changes-to-start-and-default-config\">New start script, and changes to default config</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2025-12-05-changes-to-start-and-default-config",
            "title": "New start script, and changes to default config",
            "summary": "We’ve made some small changes on how to get Orbital running. We’ve also added a new config variable to centralize where Orbital stores all it’s internal files. You should add this config variable to your own deployments before 0.37 is released.",
            "image": "https://orbitalhq.com/changelog-cards/2025-12-05-changes-to-start-and-default-config.png",
            "date_modified": "2025-12-05T00:00:00.000Z",
            "date_published": "2025-12-05T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2025-12-02-release-announcement-0.36.2",
            "content_html": "<p>Orbital 0.36.2 is a patch release that fixes a critical issue with lineage persistence.</p>\n<h2 id=\"bug-fixes\"><a href=\"#bug-fixes\">​<span></span></a>Bug Fixes</h2>\n<ul role=\"list\">\n<li>Fixed broken persistence in lineage, caused by attempting to write taxi definitions (<a href=\"https://linear.app/orbital/issue/ORB-1052\" target=\"_blank\" rel=\"noreferrer\">ORB-1052</a>)</li>\n</ul>\n<p>There are no other functional changes in this release.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2025-12-02-release-announcement-0.36.2\">0.36.2 released</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2025-12-02-release-announcement-0.36.2",
            "title": "0.36.2 released",
            "summary": "Orbital 0.36.2 is a patch release that fixes a critical issue with lineage persistence.",
            "image": "https://orbitalhq.com/changelog-cards/2025-12-02-release-announcement-0.36.2.png",
            "date_modified": "2025-12-02T00:00:00.000Z",
            "date_published": "2025-12-02T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2025-11-11-release-announcement-0.36.1",
            "content_html": "<p>Orbital 0.36.1 is a patch release that addresses memory leaks.</p>\n<p>There are no other functional changes in this release.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2025-11-11-release-announcement-0.36.1\">0.36.1 released</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2025-11-11-release-announcement-0.36.1",
            "title": "0.36.1 released",
            "summary": "Orbital 0.36.1 is a patch release that addresses memory leaks.",
            "image": "https://orbitalhq.com/changelog-cards/2025-11-11-release-announcement-0.36.1.png",
            "date_modified": "2025-11-11T00:00:00.000Z",
            "date_published": "2025-11-11T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2025-08-04-improved-array-type-checking",
            "content_html": "<p>Orbital 0.36 introduces a fix to type checking with array arguments.</p>\n<p>Previously, arrays were not being correctly type checked, such that this was possible:</p>\n<section><div><div><span>Show schema</span><div><a href=\"https://playground.taxilang.org\" target=\"_blank\" rel=\"noreferrer\"><span>Edit this on Taxi Playground</span></a><span>Run this query</span></div></div><div><div><textarea id=\"_R_aqqj5_-query\" rows=\"4\" wrap=\"off\" spellCheck=\"false\">import Actor\nfind { \n   films: Film[] = Film[] as Actor[]\n}</textarea></div></div></div></section>\n<p>The same type checking would also fail with arguments to functions or expression types:</p>\n<section><div><div><span>Show schema</span><div><a href=\"https://playground.taxilang.org\" target=\"_blank\" rel=\"noreferrer\"><span>Edit this on Taxi Playground</span></a><span>Run this query</span></div></div><div><div><textarea id=\"_R_iqqj5_-query\" rows=\"5\" wrap=\"off\" spellCheck=\"false\">import Actor\ngiven { actors: Actor[] = [{name: &quot;Jim&quot;}]}\nfind { \n   films: Film = onlyOneFilm(actors)\n}</textarea></div></div></div></section>\n<p>These now correctly return compilation errors.</p>\n<p>This is a breaking change, as these statements did not used to return compilation errors (but would not behave as expected).</p>\n<p>This change will be released in 0.36, and is available from 0.36.0-M9</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2025-08-04-improved-array-type-checking\">Breaking change - improved type checking with arrays in 0.36</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2025-08-04-improved-array-type-checking",
            "title": "Breaking change - improved type checking with arrays in 0.36",
            "summary": "Orbital 0.36 introduces a fix to type checking with array arguments.",
            "image": "https://orbitalhq.com/changelog-cards/2025-08-04-improved-array-type-checking.png",
            "date_modified": "2025-08-04T00:00:00.000Z",
            "date_published": "2025-08-04T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2025-05-07-release-announcement-0-35-0",
            "content_html": "<figure><img src=\"https://orbitalhq.com/assets/title-card-DCtABlAo.jpg\" alt=\"\"/><span></span></figure><p>Orbital 0.35.0 brings significant enhancements to core capabilities, with major improvements to streaming,\ncaching, and model handling.</p>\n<p>This release focuses on stability and performance, particularly for high-volume data processing scenarios.</p>\n<h2 id=\"partial-models\"><a href=\"#partial-models\">​<span></span></a>Partial models</h2>\n<p>Partial models create variations of existing models where all fields are optional. This pattern is especially useful for API operations that support partial updates (PATCH), allowing clients to send only the fields they want to change.</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>model</span><span> </span><span>Customer</span><span> </span><span>{</span></span><span><span>   id</span><span>:</span><span> CustomerId</span><span> </span><span>inherits</span><span> </span><span>String</span></span><span><span>   name</span><span>:</span><span> CustomerName</span><span> </span><span>inherits</span><span> </span><span>String</span></span><span><span>}</span></span><span><span>   </span></span><span><span>// Creates a new model where all fields are optional</span></span><span><span>partial </span><span>model</span><span> </span><span>CustomerUpdate</span><span> from Customer</span></span></code></pre></div></div>\n<p>When you create a partial model, all fields become optional (nullable), including:</p>\n<ul role=\"list\">\n<li>Nested objects: Automatically converted to partial versions</li>\n<li>Collections: Arrays become arrays of partial versions</li>\n<li>Self-references: Updated to reference the partial version</li>\n<li>Annotations from the source model are merged with annotations on the partial model</li>\n<li>Modifiers are inherited with one exception: partial models cannot be <code>closed</code></li>\n</ul>\n<p>This all combines well with the @OmitNulls annotation to ensure null fields are excluded from serialized payloads:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>// Original model with required fields</span></span><span><span>model</span><span> </span><span>Product</span><span> </span><span>{</span></span><span><span>   id</span><span>:</span><span> ProductId</span><span> </span><span>inherits</span><span> </span><span>String</span></span><span><span>   name</span><span>:</span><span> ProductName</span><span> </span><span>inherits</span><span> </span><span>String</span></span><span><span>   price</span><span>:</span><span> Price</span><span> </span><span>inherits</span><span> </span><span>Decimal</span></span><span><span>   category</span><span>:</span><span> Category</span></span><span><span>   tags</span><span>:</span><span> Tag</span><span>[</span><span>]</span></span><span><span>}</span></span><span>\n</span><span><span>// Create a partial version where everything is optional</span></span><span><span>@</span><span>OmitNulls</span></span><span><span>partial </span><span>model</span><span> </span><span>ProductUpdate</span><span> from Product</span></span><span>\n</span><span><span>service</span><span> </span><span>ProductCatalog</span><span> </span><span>{</span></span><span><span>   </span><span>// Full replacement (PUT)</span></span><span><span>   </span><span>write</span><span> </span><span>operation</span><span> </span><span>replaceProduct</span><span>(</span><span>Product</span><span>)</span><span>:</span><span> Product</span></span><span><span>   </span></span><span><span>   </span><span>// Partial update (PATCH)</span></span><span><span>   </span><span>write</span><span> </span><span>operation</span><span> </span><span>updateProduct</span><span>(</span><span>ProductUpdate</span><span>)</span><span>:</span><span> Product</span></span><span><span>}</span></span></code></pre></div></div>\n<h2 id=\"openapi-avro-and-protobuf-in-a-taxi-project\"><a href=\"#openapi-avro-and-protobuf-in-a-taxi-project\">​<span></span></a>OpenAPI, Avro and Protobuf in a taxi project</h2>\n<p>We now support including OpenAPI, Avro and Protobuf files directly within a taxi project.</p>\n<p>Add entries in the <code>additionalSources</code> block of your <code>taxi.conf</code>, as shown below:</p>\n<div><div><div><span>taxi.conf</span></div><div><span></span><span>Copy</span></div></div><div><pre><code><span><span>name</span><span>:</span><span> taxi/mixed</span><span>-</span><span>sources</span></span><span><span>version</span><span>:</span><span> 0.3.0</span></span><span><span>sourceRoot</span><span>:</span><span> src/</span></span><span><span>additionalSources</span><span>:</span><span> </span><span>{</span></span><span><span>   &quot;@orbital/avro&quot; = &quot;avro/src/</span><span>*.avsc&quot;</span></span><span><span>   &quot;@orbital/openapi&quot; = &quot;openapi/</span><span>*.yaml&quot;</span></span><span><span>   &quot;@orbital/protobuf&quot; = &quot;proto/src/</span><span>**/*.proto&quot;</span></span><span><span>}</span></span></code></pre></div></div>\n<p>These sources are automatically read, along with their taxi annotations, and imported into your taxi project.</p>\n<p>This simplifies setups (and startup times) for teams where each spec was  previously imported as standalone projects.</p>\n<h2 id=\"parallel-streaming\"><a href=\"#parallel-streaming\">​<span></span></a>Parallel streaming</h2>\n<p>When running Orbital in a cluster, background stream jobs can now be configured to run in parallel,\nwhich controls the number of nodes in the cluster that will execute the stream.</p>\n<p>using the <code>@Parallel</code> annotation.</p>\n<p>For example, assuming Orbital is deployed in a 6-node cluster, consuming events from Kafka.</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>import</span><span> com</span><span>.</span><span>orbitalhq</span><span>.</span><span>streams</span><span>.</span><span>Parallel</span></span><span><span>   </span></span><span><span>@</span><span>Parallel</span><span>(</span><span>count </span><span>=</span><span> </span><span>4</span><span>)</span></span><span><span>query</span><span> </span><span>MyStream</span><span> </span><span>{</span></span><span><span>   </span><span>stream</span><span> </span><span>{</span><span> CustomerLoggedInEvent</span><span> </span><span>}</span></span><span><span>   call MyMongoDb</span><span>:</span><span>:</span><span>upsertCustomerMetrics</span></span><span><span>}</span></span></code></pre></div></div>\n<p>In this configuration, 4 of the nodes will subscribe to Kafka, and consume the events.</p>\n<p>Orbital correctly rebalances as nodes enter and leave the cluster.</p>\n<p>Learn more in the <a href=\"https://orbitalhq.com/docs/querying/streaming-data#controlling-parallelism\">streaming data docs</a></p>\n<h2 id=\"improved-caching-controls\"><a href=\"#improved-caching-controls\">​<span></span></a>Improved caching controls</h2>\n<p>The <code>@com.orbitalhq.caching.Cache</code> annotation offers control over caching behavior at both operation and model\nlevels.</p>\n<p>You can disable caching for specific operations or data types, or adjust cache expiration\nwith configurable time-to-live settings.</p>\n<p>This helps balance performance needs when working with both static reference data and\nchanging values in long-running stream processors.</p>\n<p>Read more about configuring caching in the <a href=\"https://orbitalhq.com/docs/querying/caching\">caching docs</a></p>\n<h2 id=\"azure-service-bus\"><a href=\"#azure-service-bus\">​<span></span></a>Azure service bus</h2>\n<p>We added support for Azure Service Bus, enabling integration with Azure messaging infrastructure\nthrough a full-featured connector.</p>\n<p>Read more in the <a href=\"https://orbitalhq.com/docs/describing-data-sources/azure-service-bus\">Azure Service Bus docs</a></p>\n<h2 id=\"other-updates\"><a href=\"#other-updates\">​<span></span></a>Other updates</h2>\n<h3 id=\"enhanced-model--query-capabilities\"><a href=\"#enhanced-model--query-capabilities\">​<span></span></a>Enhanced Model &amp; Query Capabilities</h3>\n<ul role=\"list\">\n<li><strong>Nested Projections with Multiple Scopes</strong>: Fixed support for nested projections with multiple scope variables, eliminating the “How to handle multiple scoped facts here?” error.</li>\n<li><strong>Operation Input Improvements</strong>: Operations can now accept inputs populated by supertypes, enabling more flexible service invocation.</li>\n<li>Added ability to use expressions and constraints on the left-hand side of type references. [ORB-932]</li>\n<li>Object build type searches now correctly consider constraints. [ORB-895]</li>\n<li>Improved support for constraints on nested chained projections. [ORB-897]</li>\n<li><strong>Spread Operator Improvements</strong>: Fixed spread operator to properly consider inherited types. [ORB-891]</li>\n</ul>\n<h3 id=\"streaming-enhancements\"><a href=\"#streaming-enhancements\">​<span></span></a>Streaming Enhancements</h3>\n<ul role=\"list\">\n<li>Added low-cardinality error reporting for streams, providing better visibility into failures during stream processing.</li>\n<li>Fixed error reporting in streams so stream failures are accurately reported in the UI. [ORB-868]</li>\n<li>Streaming queries with a mutation operation now continue even if the mutation operation fails. [ORB-934]</li>\n</ul>\n<h3 id=\"http-improvements\"><a href=\"#http-improvements\">​<span></span></a>HTTP Improvements</h3>\n<ul role=\"list\">\n<li><strong>HTTP Retry Reporting</strong>: Fixed reporting of retried HTTP service calls in the profile window, improving visibility of retry attempts for operations with the <code>@HttpRetry</code> annotation. [ORB-931]</li>\n</ul>\n<h3 id=\"operational-improvements\"><a href=\"#operational-improvements\">​<span></span></a>Operational Improvements</h3>\n<ul role=\"list\">\n<li><strong>Improved Error Reporting</strong>: Enhanced error reporting when variables are missing in queries, providing more meaningful feedback to developers.</li>\n<li><strong>Metrics &amp; Monitoring</strong>: Multiple improvements to stream observability, including better error reporting, status monitoring, and metrics collection.</li>\n</ul>\n<h3 id=\"file-monitoring\"><a href=\"#file-monitoring\">​<span></span></a>File Monitoring</h3>\n<ul role=\"list\">\n<li><strong>Git Project File Watching</strong>: Added support for polling file watchers with Git projects, providing more reliable file change detection in certain environments. [ORB-865]</li>\n<li><strong>Empty Workspace Configuration</strong>: Added ability to configure file monitoring settings for automatically created empty workspaces.</li>\n<li><strong>Authentication Improvements</strong>: Fixed issue where updates in auth.conf were not reflected in Orbital Query Execution. [ORB-943]</li>\n</ul><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2025-05-07-release-announcement-0-35-0\">Orbital 0.35 - Partial models, mixed-source projects, parallel streaming, improved caching</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2025-05-07-release-announcement-0-35-0",
            "title": "Orbital 0.35 - Partial models, mixed-source projects, parallel streaming, improved caching",
            "summary": "Orbital 0.35 - Partial models, mixed-source projects, parallel streaming, improved caching",
            "image": "https://orbitalhq.com/assets/title-card-DCtABlAo.jpg",
            "date_modified": "2025-05-07T00:00:00.000Z",
            "date_published": "2025-05-07T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2024-07-24-release-announcement-0-33-0",
            "content_html": "<figure><img src=\"https://orbitalhq.com/assets/0-33-title-card-Gjg5NP06.jpg\" alt=\"\"/><span></span></figure><h2 id=\"orbital-0330---growing-pains-edition\"><a href=\"#orbital-0330---growing-pains-edition\">​<span></span></a>Orbital 0.33.0 - “Growing Pains” Edition</h2>\n<p>Orbital 0.33 is out - with lots (and lots) of small, incremental improvements to the features you already know and love.</p>\n<p>Sure, we skipped 0.32 - but who hasn’t had an episode in early 30’s they forgot?</p>\n<p>Because this release is all about beloved features growing, and squashing bug fixes, and because we made a\nbold choice shortly after Dry January finished to name each of this year’s releases after an 80’s sitcom,\nwe’re proud to present Orbital 0.33 - The Growing Pains edition.</p>\n<h2 id=\"highlights\"><a href=\"#highlights\">​<span></span></a>Highlights</h2>\n<ul role=\"list\">\n<li>Improved UX: <a href=\"#a-new-landing-experience\">landing page</a> and <a href=\"#improved-query-editor-experience\">query editor improvements</a></li>\n<li>Orbital is simpler to deploy, as we’ve <a href=\"#stream-server-is-now-folded-into-orbital\">merged the Stream Server into Orbital</a></li>\n<li><a href=\"#improved-s3-connector\">Improved S3 connector</a></li>\n<li><a href=\"#streaming-queries-are-observable-via-http-endpoints\">Streaming queries observable over Http endpoints</a></li>\n<li><a href=\"#avro-format\">Avro support</a></li>\n<li>Lots of <a href=\"#bug-fixes\">bug fixes</a></li>\n</ul>\n<h3 id=\"a-new-landing-experience\"><a href=\"#a-new-landing-experience\">​<span></span></a>A new landing experience</h3>\n<p>We’ve completely redesigned the landing page and now surface insights into the health of your project’s connections,\nan at a glance overview of schema changes, as well as metrics on currently running services.</p>\n<p>We’ll be adding more content in this area as well, to help get new users up to speed,\nand provide more experienced users with documentation and tutorials to improve their Orbital productivity. Watch this space!</p>\n<h3 id=\"improved-query-editor-experience\"><a href=\"#improved-query-editor-experience\">​<span></span></a>Improved Query Editor experience</h3>\n<p>You asked for it, and we listened - tabs are now part of the query editor experience!</p>\n<figure><img src=\"https://orbitalhq.com/assets/query-editor-BwUgCj7Y.png\" alt=\"Tabs FTW!\"/><span></span><figcaption><span>Tabs FTW!</span></figcaption></figure>\n<p>Create as many queries as you like, safe in the knowledge that you can tweak and edit them in isolation,\nand run the query when you’re satisfied things are looking good.</p>\n<p>Results are retained for each tab, allowing some serious multitasking.</p>\n<p>We’ve also overhauled the query editor’s toolbar, allowing you to <a href=\"#streaming-queries-are-observable-via-http-endpoints\">publish queries to endpoints</a>, and remove them\nif they’ve already been published. Alongside that, you’re now able to see which queries have endpoints associated with them, launch the endpoint,\nor copy the endpoint URL or code for a cURL command.</p>\n<p>We’re confident these UI additions further improve the developer experience, let us know what other improvements in this space you’d like to see.</p>\n<h3 id=\"stream-server-is-now-folded-into-orbital\"><a href=\"#stream-server-is-now-folded-into-orbital\">​<span></span></a>Stream server is now folded into Orbital</h3>\n<p>The Stream Server historically was a standalone service that was responsible for executing long-running streaming queries.</p>\n<p>It’s now folded directly into Orbital, simplifying the deployment significantly.</p>\n<h3 id=\"improved-s3-connector\"><a href=\"#improved-s3-connector\">​<span></span></a>Improved S3 connector</h3>\n<p>Our S3 connector received a much-needed makeover.</p>\n<p>When reading, we now support reading from either a single file, an entire bucket, or any type of wildcard glob pattern. Files on S3 are also now supported as a standard write target.</p>\n<p>We also support reading and writing to S3 in any of Orbital’s <a href=\"https://orbitalhq.com/docs/data-formats/overview\" target=\"_blank\" rel=\"noreferrer\">supported formats</a>.</p>\n<p>This means you can now do things like query a database, enrich it against a series of APIs, and write the results to a csv file on an S3 bucket. Alternatively, you can read a CSV file from S3, split it, enrich each record and write the values to Kafka.</p>\n<p>For example:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>// Given a model of data stored on S3:</span></span><span><span>@</span><span>Csv</span></span><span><span>model</span><span> </span><span>TradeSummary</span><span> </span><span>{</span></span><span><span>   symbol</span><span> </span><span>:</span><span> Symbol</span><span> </span><span>inherits</span><span> </span><span>String</span></span><span><span>   open</span><span> </span><span>:</span><span> OpenPrice</span><span> </span><span>inherits</span><span> </span><span>Decimal</span></span><span><span>   high</span><span> </span><span>:</span><span> HighPrice</span><span> </span><span>inherits</span><span> </span><span>Decimal</span></span><span><span>   close</span><span> </span><span>:</span><span> ClosePrice</span><span> </span><span>inherits</span><span> </span><span>Decimal</span></span><span><span>}</span></span><span>\n</span><span><span>// Expose the S3 bucket:</span></span><span><span>@</span><span>S3Service</span><span>(</span><span>connectionName </span><span>=</span><span> </span><span>&quot;MyAwsConnection&quot;</span><span>)</span></span><span><span>service</span><span> </span><span>AwsBucketService</span><span> </span><span>{</span></span><span><span>    </span><span>@</span><span>S3Operation</span><span>(</span><span>bucket </span><span>=</span><span> </span><span>&quot;MyTrades&quot;</span><span>)</span></span><span><span>    </span><span>operation</span><span> </span><span>readBucket</span><span>(</span><span>filename</span><span>:</span><span>FilenamePattern</span><span> </span><span>=</span><span> </span><span>&quot;trades.csv&quot;</span><span>)</span><span>:</span><span>TradeSummary</span><span>[</span><span>]</span></span><span><span>}</span></span><span>\n</span><span><span>// Expose some enrichment services:</span></span><span><span>service</span><span> </span><span>TradeVolumeApi</span><span> </span><span>{</span></span><span><span>   </span><span>@</span><span>HttpOperation</span><span>(</span><span>url</span><span>=</span><span>&quot;https://myTradeService/volumes/{symbol}&quot;</span><span>,</span><span> method </span><span>=</span><span> </span><span>&quot;GET&quot;</span><span>)</span></span><span><span>   </span><span>operation</span><span> </span><span>getTradeVolumes</span><span>(</span><span>@</span><span>PathVariable</span><span>(</span><span>&quot;symbol&quot;</span><span>)</span><span> symbol</span><span> </span><span>:</span><span> Symbol</span><span>)</span><span>:</span><span> TradeVolumes</span></span><span><span>}</span></span><span>\n</span><span><span>// Expose our enriched data model:</span></span><span><span>model</span><span> </span><span>TradesWithVolumes</span><span> </span><span>{</span></span><span><span>  symbol</span><span> </span><span>:</span><span> Symbol</span></span><span><span>  prices </span><span>:</span><span>  </span><span>{</span></span><span><span>     open</span><span> </span><span>:</span><span> OpenPrice</span></span><span><span>     close</span><span> </span><span>:</span><span> ClosePrice</span></span><span><span>  </span><span>}</span></span><span><span>  volumes </span><span>:</span><span> </span><span>{</span></span><span><span>    volumes</span><span> </span><span>:</span><span> TradeVolumes</span></span><span><span>  </span><span>}</span></span><span><span>}</span></span><span>\n</span><span><span>// And a Kafka topic to write to:</span></span><span><span>service</span><span> </span><span>MyKafkaBroker</span><span> </span><span>{</span></span><span><span>   </span><span>@</span><span>KafkaOperation</span><span>(</span><span>topic </span><span>=</span><span> </span><span>&quot;tradesWithVolumes&quot;</span><span>)</span></span><span><span>   </span><span>write</span><span> </span><span>operation</span><span> </span><span>publishTrades</span><span>(</span><span>TradesWithVolumes</span><span>)</span><span>:</span><span>TradesWithVolumes</span></span><span><span>}</span></span><span>\n</span><span><span>// Given the above, we can fetch from S3, transform against our</span></span><span><span>// enrichment API, and write the results to Kafka using the following</span></span><span><span>// query:</span></span><span><span>find</span><span> </span><span>{</span><span> TradeSummary</span><span>[</span><span>]</span><span> </span><span>)</span></span><span><span>call MyKafkaBroker</span><span>:</span><span>:</span><span>publishTrades</span></span></code></pre></div></div>\n<p>See more examples in the docs for the <a href=\"https://orbitalhq.com/docs/describing-data-sources/aws-services#s3\">S3 connector</a>.</p>\n<h3 id=\"kafka-headers-and-message-key-are-now-available\"><a href=\"#kafka-headers-and-message-key-are-now-available\">​<span></span></a>Kafka headers and message key are now available</h3>\n<p>You can now access the message key for a Kafka message directly in your model:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>import</span><span> com</span><span>.</span><span>orbitalhq</span><span>.</span><span>kafka</span><span>.</span><span>KafkaMessageKey</span></span><span>\n</span><span><span>model</span><span> </span><span>Movie</span><span> </span><span>{</span></span><span><span>  </span><span>// The key from Kafka will be read into the id property</span></span><span><span>  </span><span>@</span><span>KafkaMessageKey</span></span><span><span>  id</span><span> </span><span>:</span><span> MovieId</span><span> </span><span>inherits</span><span> </span><span>String</span></span><span><span>  title</span><span> </span><span>:</span><span> Title</span><span> </span><span>inherits</span><span> </span><span>String</span></span><span><span>}</span></span></code></pre></div></div>\n<p>Similarly, Kafka message headers can now be provided using the @KafkaMessageMetadata annotation</p>\n<p>More details are available in the updated <a href=\"https://orbitalhq.com/docs/describing-data-sources/kafka#keys-headers-and-metadata\">Kafka documentation</a></p>\n<h3 id=\"streaming-queries-are-observable-via-http-endpoints\"><a href=\"#streaming-queries-are-observable-via-http-endpoints\">​<span></span></a>Streaming queries are observable via Http endpoints</h3>\n<p>Orbital has supported saved streaming queries for some time. These run in the background doing things like consuming from\na Kafka topic (amongst others), transforming, enriching and writing data.</p>\n<p>Now, these same queries can be observed via HTTP endpoints - using either Server Sent Events or Websockets. Simply add an annotation to your query definition:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>@</span><span>HttpOperation</span><span>(</span><span>url </span><span>=</span><span> </span><span>&#x27;/api/q/newReleases&#x27;</span><span>,</span><span> method </span><span>=</span><span> </span><span>&#x27;GET&#x27;</span><span>)</span></span><span><span>query</span><span> </span><span>getNewReleaseAnnouncements</span><span> </span><span>{</span></span><span><span>  </span><span>stream</span><span> </span><span>{</span><span> NewReleaseAnnouncement</span><span> </span><span>}</span><span> </span><span>as</span><span> </span><span>{</span></span><span><span>    title</span><span> </span><span>:</span><span> MovieTitle</span></span><span><span>    reviewScore</span><span> </span><span>:</span><span> ReviewScore</span></span><span><span>  </span><span>}</span><span>[</span><span>]</span></span><span><span>}</span></span></code></pre></div></div>\n<p>These annotations can also be added by using the new publish endpoint feature detailed in the <a href=\"#improved-query-editor-experience\">query editor improvements</a></p>\n<p>This query would be available over Curl using</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>curl</span><span> </span><span>-X</span><span> GET </span><span>&#x27;http://localhost:9022/api/q/newReleases&#x27;</span><span> </span><span>\\</span></span><span><span>  </span><span>-H</span><span> </span><span>&#x27;Accept: text/event-stream;charset-UTF-8&#x27;</span></span></code></pre></div></div>\n<p>More details are available in the documentation for <a href=\"https://orbitalhq.com/docs/querying/queries-as-endpoints#saved-streams\">saved streaming queries</a>.</p>\n<h3 id=\"avro-format\"><a href=\"#avro-format\">​<span></span></a>Avro format</h3>\n<p>We’ve added support for Avro as a format - both reading and writing.</p>\n<p>Additionally, we also support Confluent’s own, special, <a href=\"https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/serdes-avro.html\" target=\"_blank\" rel=\"noreferrer\">magical version of Avro</a>, which injects a few additional bytes at\nthe start … “magic bytes” — their words, not ours. Anyway, we support reading this, without a dependency on the Confluent schema server.</p>\n<p>Find out more about Avro support in the <a href=\"https://orbitalhq.com/docs/data-formats/avro\">Avro documentation</a></p>\n<h3 id=\"batched-writes-for-mongo\"><a href=\"#batched-writes-for-mongo\">​<span></span></a>Batched writes for Mongo</h3>\n<p>Orbital can now batch upserts target to a Mongo Collections. See the <a href=\"https://orbitalhq.com/docs/describing-data-sources/mongodb#batching-upsert-operations\">documentation for details.</a></p>\n<h3 id=\"batching-mutations\"><a href=\"#batching-mutations\">​<span></span></a>Batching mutations</h3>\n<p>Previously, mutations were run incrementally in parallel.</p>\n<p>You can now optionally indicate that a mutation should be performed at the end of the query,\nafter all projections have been completed. This is useful for targets like S3, where incremental writing isn’t supported.</p>\n<p>More information and examples are available in our updated docs on <a href=\"https://orbitalhq.com/docs/querying/mutations#writing-single-item-vs-batch\">mutations</a>.</p>\n<h3 id=\"other-features\"><a href=\"#other-features\">​<span></span></a>Other features:</h3>\n<ul role=\"list\">\n<li><a href=\"https://orbitalhq.com/docs/describing-data-sources/kafka#additional-kafka-connection-properties\">Configurable groupId for a Kafka connection</a></li>\n<li><a href=\"https://orbitalhq.com/docs/describing-data-sources/hazelcast#advanced-configuration\">Better configuration options for Hazelcast connections</a></li>\n</ul>\n<h2 id=\"bug-fixes\"><a href=\"#bug-fixes\">​<span></span></a>Bug Fixes</h2>\n<h3 id=\"performance\"><a href=\"#performance\">​<span></span></a>Performance</h3>\n<p>There’s been a number of performance improvements across the stack:</p>\n<ul role=\"list\">\n<li>Fixed bottleneck when queries don’t use a projection in a mutation</li>\n<li>Fixed performance writing to Hazelcast maps</li>\n<li>Fixed performance issues with @Cache annotated queries</li>\n<li>A number of minor tweaks to the core query engine to improve performance</li>\n<li>We’ve fixed issues making it very slow to parse database metadata (when importing a database table)</li>\n</ul>\n<h3 id=\"relative-paths-in-workspaceconf\"><a href=\"#relative-paths-in-workspaceconf\">​<span></span></a>Relative paths in workspace.conf</h3>\n<p>Some customers like to manually deploy their taxi projects onto the same box that Orbital is running on, and refer to the taxi projects using file paths (rather than pulling from a git repository).</p>\n<p>In cases like this, it’s often convenient to declare your projects in workspace.conf with relative paths (relative to the workspace.conf file), rather than absolute paths. We’ve fixed a number of bugs that previously made this very hit-and-miss. Now, it’s all hits.</p>\n<h3 id=\"pagination-in-query-history\"><a href=\"#pagination-in-query-history\">​<span></span></a>Pagination in query history</h3>\n<p>Previously, we only showed the first 100 results in query history. We now paginate these, so the full result set is available.</p>\n<h3 id=\"websockets-timing-out\"><a href=\"#websockets-timing-out\">​<span></span></a>Websockets timing out</h3>\n<p>We fixed a number of issues with websockets timing out - this was particularly noticeable if Orbital was deployed in AWS behind an ALB, where the ALB would terminate websocket connections after a period of inactivity. We’ve added ping/pong on the websockets to prevent this.</p>\n<h3 id=\"ability-to-remove-projects-pushed-to-orbital\"><a href=\"#ability-to-remove-projects-pushed-to-orbital\">​<span></span></a>Ability to remove projects pushed to Orbital</h3>\n<p>Often, a microservice will publish it’s project directly to Orbital. Previously, these weren’t removable using the UI. Now they are. That’s progress.</p>\n<h3 id=\"fixed-cast-expressions-on-a-field\"><a href=\"#fixed-cast-expressions-on-a-field\">​<span></span></a>Fixed cast expressions on a field</h3>\n<p>This used to throw an error. Now it doesn’t.</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span></span><span><span>    long</span><span>:</span><span> Long</span><span> </span><span>by</span><span> </span><span>(</span><span>Long</span><span>)</span><span> Foo</span></span><span><span>}</span></span></code></pre></div></div>\n<h3 id=\"projecting-a-scoped-variable-to-a-predefined-array-type\"><a href=\"#projecting-a-scoped-variable-to-a-predefined-array-type\">​<span></span></a>Projecting a scoped variable to a predefined array type</h3>\n<p>This also used to throw an error. We fixed that too.</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> FilmCatalog</span><span> </span><span>}</span><span> </span><span>as</span><span> </span><span>(</span><span>films</span><span>:</span><span>Film</span><span>[</span><span>]</span><span>)</span><span> </span><span>-</span><span>&gt;</span><span> Movie</span><span>[</span><span>]</span></span></code></pre></div></div>\n<h3 id=\"lots-of-bug-fixes-with-git-based-projects\"><a href=\"#lots-of-bug-fixes-with-git-based-projects\">​<span></span></a>Lots of bug fixes with Git based projects</h3>\n<p>We squashed lots of bugs related to workspaces that were pulled from git, and projects within workspaces that were pulled from git.\nThis now behaves much much better.</p>\n<h3 id=\"sqs-subscriber-fixes\"><a href=\"#sqs-subscriber-fixes\">​<span></span></a>SQS Subscriber fixes</h3>\n<p>We have fixed an SQS subscription that might result in corresponding poll operations interrupted after the first successful poll.\nAdditional configuration parameters to fine tune AWS SQS poll operations were also added, see the <a href=\"https://orbitalhq.com/docs/describing-data-sources/aws-services#consuming-events\" target=\"_blank\" rel=\"noreferrer\">docs for the details.</a></p>\n<h3 id=\"changes-to-workspaceconf-are-now-live-reloaded\"><a href=\"#changes-to-workspaceconf-are-now-live-reloaded\">​<span></span></a>Changes to workspace.conf are now live reloaded</h3>\n<p>This bug manifested in lots of little, but rather annoying changes.</p>\n<p>We now automatically update the UI after changes are made to the workspace.conf file (such as adding and removing projects).</p>\n<h3 id=\"other-bug-fixes\"><a href=\"#other-bug-fixes\">​<span></span></a>Other bug fixes:</h3>\n<ul role=\"list\">\n<li>We’ve addressed issues with Logout not working as expected on Cognito</li>\n<li>Importing a Protobuf spec from the UI now works</li>\n<li>Saving a query no longer breaks the formatting</li>\n<li>Language server now reconnects automatically after disconnect</li>\n<li>Cancelling a streaming query from the query history view works again</li>\n<li>Fixed a bug where JSON results viewer would show type hints over and over and over</li>\n<li>Download query results as CSV works again</li>\n<li>Browser would crash when running queries returning large volumes of results</li>\n<li>A misconfigured workspace.conf file could prevent the server from starting</li>\n</ul><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2024-07-24-release-announcement-0-33-0\">Orbital 0.33 - Landing page insights, improved query editor, and simpler deployments.</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2024-07-24-release-announcement-0-33-0",
            "title": "Orbital 0.33 - Landing page insights, improved query editor, and simpler deployments.",
            "summary": "Orbital 0.33 - Landing page insights, improved query editor, and simpler deployments.",
            "image": "https://orbitalhq.com/assets/0-33-title-card-Gjg5NP06.jpg",
            "date_modified": "2024-07-24T00:00:00.000Z",
            "date_published": "2024-07-24T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2024-04-05-release-announcement-0-31-0",
            "content_html": "<figure><img src=\"https://orbitalhq.com/assets/0-31-title-card-BI6HYHTz.jpg\" alt=\"\"/><span></span></figure><h2 id=\"orbital-0310---alf-edition\"><a href=\"#orbital-0310---alf-edition\">​<span></span></a>Orbital 0.31.0 - “Alf” Edition</h2>\n<p>We’re excited\nto announce the release of Orbital <a href=\"https://hub.docker.com/layers/orbitalhq/orbital/0.31.0/images/sha256-62c195eed42025fec0d22debcdbc6c0d9e250425eeb37b34a92e825ffae4f416?context=explore\" target=\"_blank\" rel=\"noreferrer\">0.31.0</a> -\nwith a large focus on helping new users feel comfortable — which reminded us of a certain ’80s friend.\nSo, we present Orbital 0.31.0 - The “Alf” edition. 👽🛸🏡</p>\n<h2 id=\"highlights\"><a href=\"#highlights\">​<span></span></a>Highlights</h2>\n<ul role=\"list\">\n<li>New connectors: <a href=\"#mongodb-support\">MongoDB</a> and <a href=\"#hazelcast-support\">Hazelcast</a></li>\n<li><a href=\"#new-onboarding-experience\">New onboarding flow</a>, and <a href=\"#improved-ux-for-managing-projects-and-data-sources\">improved UX for projects and data sources</a></li>\n<li><a href=\"#filtering-a-stream\">Filtering streams of data</a></li>\n<li><a href=\"#controlling-services-used-in-a-query-preview\">Controlling the services called in a query</a></li>\n</ul>\n<h3 id=\"mongodb-support-preview\"><a href=\"#mongodb-support-preview\">​<span></span></a>MongoDB support (Preview)</h3>\n<p>We now support <a href=\"https://orbitalhq.com/docs/describing-data-sources/mongodb\">MongoDB</a> as a datasource, including support for both\n<a href=\"https://orbitalhq.com/docs/describing-data-sources/mongodb#querying-collections\">querying</a> and <a href=\"https://orbitalhq.com/docs/describing-data-sources/mongodb#writing-data-to-a-collection\">writing</a></p>\n<h3 id=\"hazelcast-support-preview\"><a href=\"#hazelcast-support-preview\">​<span></span></a>Hazelcast support (Preview)</h3>\n<p>Building on top of our support for <a href=\"https://hazelcast.com\" target=\"_blank\" rel=\"noreferrer\">Hazelcast</a> as a <a href=\"https://orbitalhq.com/docs/querying/caching\">cache</a>,\nHazelcast is now available as a data source, supporting reads, writes and streams.</p>\n<p>Use a Hazelcast Map as a <a href=\"https://orbitalhq.com/docs/describing-data-sources/hazelcast#writing-data-to-hazelcast\">high speed data store</a>,\nor use a Map as the basis for a <a href=\"https://orbitalhq.com/docs/describing-data-sources/hazelcast#streaming-data-from-hazelcast\">Stream of data</a>,\ngiving you the best of both worlds.</p>\n<h3 id=\"new-onboarding-experience\"><a href=\"#new-onboarding-experience\">​<span></span></a>New onboarding experience</h3>\n<p>For those wanting to dip their toes into the world of Taxi and Orbital, we’ve built an onboarding flow which should help\nget you up to speed in no time! And remember, just like Alf trying to fit in with the Tanners, you might feel a bit out\nof place at first, but with our guidance, you’ll be navigating through like a pro in no time!</p>\n<p>You’ll see this screen automatically when you don’t have any data sources declared, but you can also view it manually by\ngoing to /onboarding.</p>\n<figure><img src=\"https://orbitalhq.com/assets/onboarding-BYhHAFME.png\" alt=\"The new onboarding flow\"/><span></span><figcaption><span>The new onboarding flow</span></figcaption></figure>\n<p>We welcome feedback in this area of the app, so please take it for a spin and let us know what you think.</p>\n<h3 id=\"improved-ux-for-managing-projects-and-data-sources\"><a href=\"#improved-ux-for-managing-projects-and-data-sources\">​<span></span></a>Improved UX for managing projects and data sources</h3>\n<p>Now that you’re familiar with adding projects and data sources after completing the onboarding steps, you’ll be happy to\nhear that those same project import and data source creation steps are available to you outside the onboarding flow.\nConsistency FTW!</p>\n<p>We’ve completely reworked the Connections page, allowing a more streamlined view of all connections, and their related\ndata sources.</p>\n<figure><img src=\"https://orbitalhq.com/assets/data-source-tree-CDMdKcSQ.jpg\" alt=\"Viewing connections and data sources all from one screen\"/><span></span><figcaption><span>Viewing connections and data sources all from one screen</span></figcaption></figure>\n<p>If you want to add a new data source, then the same flow that was created for the onboarding flow is now used here as\nwell. It’s like having Alf in charge of the kitchen - he sticks to his recipes with that good ‘ol fashioned consistency\nthat we’re all about here at the house of Orbital.</p>\n<figure><img src=\"https://orbitalhq.com/assets/add-data-source-y6jRubvj.jpg\" alt=\"Adding a new data source\"/><span></span><figcaption><span>Adding a new data source</span></figcaption></figure>\n<h3 id=\"sliding-sidebars\"><a href=\"#sliding-sidebars\">​<span></span></a>Sliding Sidebars</h3>\n<p>Ever needed a few more cm’s of screen space and thought, gee that sidebar is taking up a bunch of space? Well, now you\ncan collapse it with a click of the mouse, and regain all that space you’ve been desperately hankering for. We’ll\nremember your preference as well, and restore things how you left them.</p>\n<figure><img src=\"https://orbitalhq.com/assets/sidebar-collapse-DRpusLhj.png\" alt=\"Optimise your screen real estate, all with one click!\"/><span></span><figcaption><span>Optimise your screen real estate, all with one click!</span></figcaption></figure>\n<h3 id=\"controlling-services-used-in-a-query-preview\"><a href=\"#controlling-services-used-in-a-query-preview\">​<span></span></a>Controlling services used in a query (preview)</h3>\n<p>By popular demand, one of our most requested features is the ability to control which services are called in a query.\nAfter all - if Alf taught us anything - given a universe of possibilities, a little navigation can save you landing in a whole world of trouble.</p>\n<p>While designing this feature, we wanted to ensure that Taxi remains declarative - and find a balance between giving\nusers control over which services are called, while remaining resilient and self-adapting as services evolve.</p>\n<p>You can now limit a query to <strong>only call specified services</strong>, with the <code>using</code> keyword:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> Film</span><span>[</span><span>]</span><span> </span><span>}</span><span> </span><span>as</span><span> </span><span>{</span></span><span><span>  filmId</span><span> </span><span>:</span><span> FilmId</span></span><span><span>  reviews</span><span> </span><span>:</span><span> ReviewScore</span></span><span><span>}</span></span><span><span>using </span><span>{</span><span> FilmsDb</span><span>,</span><span> NetflixApi</span><span>:</span><span>:</span><span>getReviews </span><span>}</span></span></code></pre></div></div>\n<p>The list of allowed endpoints can be a mixture of services (<code>FilmsDb</code>), or even specific operations on services\n(<code>NetflixApi::getReviews</code>).</p>\n<p>Similarly, you can allow Orbital to call any services <em>except</em> some, using the <code>excluding</code> keyword:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> Film</span><span>[</span><span>]</span><span> </span><span>}</span><span> </span><span>as</span><span> </span><span>{</span></span><span><span>  filmId</span><span> </span><span>:</span><span> FilmId</span></span><span><span>  reviews</span><span> </span><span>:</span><span> ReviewScore</span></span><span><span>}</span></span><span><span>excluding </span><span>{</span><span> ImdbApi</span><span>,</span><span> RottenTomatoes</span><span>:</span><span>:</span><span>getReviews </span><span>}</span></span></code></pre></div></div>\n<p>This too allows combining services and specific operations to exclude.</p>\n<p>This feature is considered in preview, and the syntax may change, so we’re actively seeking feedback.</p>\n<p>Please <a href=\"https://join.slack.com/t/orbitalapi/shared_invite/zt-697laanr-DHGXXak5slqsY9DqwrkzHg\" target=\"_blank\" rel=\"noreferrer\">reach out</a> to share your thoughts.</p>\n<h3 id=\"filtering-a-stream\"><a href=\"#filtering-a-stream\">​<span></span></a>Filtering a stream</h3>\n<p>We’ve added new support for <a href=\"https://orbitalhq.com/docs/querying/streaming-data#filtering-streams\">filtering streams of data</a> server-side (ie., on Orbital).</p>\n<p>Imagine you have a Kafka stream of stock quotes, and you only want\nquotes for a specific symbol - this is now possible using <code>.filterEach()</code>.</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>stream</span><span> </span><span>{</span><span> StockQuotes</span><span>.</span><span>filterEach</span><span>(</span><span> Symbol </span><span>-</span><span>&gt;</span><span> Symbol </span><span>==</span><span> </span><span>&#x27;AAPL&#x27;</span><span> </span><span>)</span><span> </span><span>}</span></span></code></pre></div></div>\n<h3 id=\"http-headers-now-visible\"><a href=\"#http-headers-now-visible\">​<span></span></a>Http headers now visible</h3>\n<p>We now display HTTP headers in the Profiler view of queries</p>\n<figure><img src=\"https://orbitalhq.com/assets/http-headers-display-oVdy6yEf.png\" alt=\"Http headers now visible\"/><span></span><figcaption><span>Http headers now visible</span></figcaption></figure>\n<h3 id=\"updates-to-stream-ux\"><a href=\"#updates-to-stream-ux\">​<span></span></a>Updates to Stream UX</h3>\n<p>The Endpoints page now shows the current status of individual streams.</p>\n<p>The Stream Page allows enabling &amp; disabling of individual streams.</p>\n<figure><img src=\"https://orbitalhq.com/assets/stream-server-overview-DI-hnKF7.png\" alt=\"Stream status now shown\"/><span></span><figcaption><span>Stream status now shown</span></figcaption></figure>\n<h3 id=\"streams-now-disabled-by-default\"><a href=\"#streams-now-disabled-by-default\">​<span></span></a>Streams now disabled by default</h3>\n<p>When a stream appears for the first time in the schema, it’s now disabled by default, and required\nmanually enabling — for the first time only.</p>\n<p>This is to prevent streams - which can be mutating - starting accidentally, because as Alf would say, ‘Hey, no one wants\naccidental mutations on their hands… or their planet!</p>\n<h3 id=\"streams-now-automatically-updated\"><a href=\"#streams-now-automatically-updated\">​<span></span></a>Streams now automatically updated</h3>\n<p>Whenever changes are found in a running streaming query, the stream\nis now automatically updated. Previously, this was buggy…Suboptimal.</p>\n<h3 id=\"cve-updates\"><a href=\"#cve-updates\">​<span></span></a>CVE Updates</h3>\n<p>This build bumps our internal dependencies, addressing the following CVE’s:</p>\n<ul role=\"list\">\n<li><a href=\"https://nvd.nist.gov/vuln/detail/CVE-2024-22257\" target=\"_blank\" rel=\"noreferrer\">CVE-2024-22257</a> Possible Broken Access Control in Spring Security With Direct Use of AuthenticatedVoter</li>\n<li><a href=\"https://nvd.nist.gov/vuln/detail/CVE-2024-22234\" target=\"_blank\" rel=\"noreferrer\">CVE-2024-22234</a> Broken Access Control in Spring Security With Direct Use of isFullyAuthenticated</li>\n<li><a href=\"https://nvd.nist.gov/vuln/detail/CVE-2024-22259\" target=\"_blank\" rel=\"noreferrer\">CVE-2024-22259</a> Spring Framework URL Parsing with Host Validation (2nd report)</li>\n<li><a href=\"https://nvd.nist.gov/vuln/detail/CVE-2024-22243\" target=\"_blank\" rel=\"noreferrer\">CVE-2024-22243</a> Spring Framework URL Parsing with Host Validation</li>\n<li><a href=\"https://nvd.nist.gov/vuln/detail/CVE-2023-52425\" target=\"_blank\" rel=\"noreferrer\">CVE-2023-52425</a> libexpat through 2.5.0 allows a denial of service</li>\n</ul>\n<aside role=\"note\"><p><span>Info</span></p><p>We upgrade our dependencies regularly to address published CVE’s. Based on our independent research, Orbital\nwas not affected by these vulnerabilities.</p></aside><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2024-04-05-release-announcement-0-31-0\">0.31 released - Hazelcast, MongoDb and a new onboarding experience</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2024-04-05-release-announcement-0-31-0",
            "title": "0.31 released - Hazelcast, MongoDb and a new onboarding experience",
            "summary": "Orbital 0.31.0 - \"Alf\" - MongoDB support, Hazelcast datasources and onboarding",
            "image": "https://orbitalhq.com/assets/0-31-title-card-BI6HYHTz.jpg",
            "date_modified": "2024-04-05T00:00:00.000Z",
            "date_published": "2024-04-05T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2024-03-08-release-announcement-0-30-0",
            "content_html": "<figure><img src=\"https://orbitalhq.com/assets/0-30-title-card-BWp82QFu.jpg\" alt=\"\"/><span></span></figure><h2 id=\"orbital-0300---full-house-edition\"><a href=\"#orbital-0300---full-house-edition\">​<span></span></a>Orbital 0.30.0 - Full House Edition</h2>\n<p>We’re excited to announce the release of Orbital <a href=\"https://hub.docker.com/layers/orbitalhq/orbital/0.30.0/images/sha256-ecffa5ece8d7e5dc6bbde2f1a47232ce393b34929415196c13ffae92d4fd6b69?context=explore\" target=\"_blank\" rel=\"noreferrer\">0.30.0</a> - our “Full House” edition, because we got so distracted by Valentine’s Day last month that we didn’t manage to ship a release, making this one double-packed.</p>\n<p>If you’re not familiar with Orbital, it’s a platform for automating integration between APIs, Databases, Message Brokers, Serverless Functions, and more.\nWe have great stuff like API-spec driven data sources, self-repairing integration, automated lineage and more.</p>\n<p>So, let’s take a look at what we’ve been working on for the past two months.\nIt’s a full house jam packed with features and squashed bugs.</p>\n<h2 id=\"highlights\"><a href=\"#highlights\">​<span></span></a>Highlights</h2>\n<h3 id=\"ms-sql-server-support\"><a href=\"#ms-sql-server-support\">​<span></span></a>MS SQL Server support</h3>\n<p>We’ve extended our family of supported databases to include MS SQL Server for comprehensive read and write operations.</p>\n<p>So, it’s “DATABASES!\nDATABASES!\nDATABASES!”, sweaty armpits, and happy Microsoft shops.</p>\n<p>Our docs explain all the details on <a href=\"https://orbitalhq.com/docs/describing-data-sources/configuring-connections#mssql-server\">how to connect to MSSQL</a>, to get your Northwind blowing.\n(Get it? That’s a MSSQL sample database joke. If you know, you know.)</p>\n<figure><img src=\"https://media1.tenor.com/m/eIJUpOJlykUAAAAC/steve-ballmer-yes.gif\" alt=\"Steve Balmer\"/><span></span><figcaption><span>Steve Balmer</span></figcaption></figure>\n<h3 id=\"saml-authentication-support\"><a href=\"#saml-authentication-support\">​<span></span></a>SAML authentication support</h3>\n<p>Someone paid us a lot of money to build SAML. We didn’t wanna do it. But we did, and now it’s awesome. Learn more about how to configure <a href=\"https://orbitalhq.com/docs/deploying/authentication#saml-authentication\">SAML authentication in Orbital</a>.</p>\n<h3 id=\"mtls-support\"><a href=\"#mtls-support\">​<span></span></a>mTLS Support</h3>\n<p>Mutual trust is a big theme in the Tanner household, and it’s something we can really get behind.</p>\n<p>mTLS is now supported when calling HTTP(s) APIs. Read more about <a href=\"https://orbitalhq.com/docs/describing-data-sources/authentication-to-services#mutual-authentication-mtls\">how to set up mTLS</a></p>\n<h3 id=\"mixed-messages\"><a href=\"#mixed-messages\">​<span></span></a>Mixed messages</h3>\n<p>Orbital will now read an XML message with a JSON message in the middle, or JSON with XML in it, or a CSV file with a JSON blog in that, or really anything inside of another thing, which is inside yet another thing.\nIt’s the integration version of a <a href=\"https://www.seriouseats.com/the-ultimate-turducken-recipe\" target=\"_blank\" rel=\"noreferrer\">Turducken</a>.</p>\n<p>For example, here’s some XML inside a JSON message:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>{</span></span><span><span>    </span><span>&quot;messageId&quot;</span><span> </span><span>:</span><span> </span><span>&quot;123&quot;</span><span>,</span></span><span><span>    </span><span>&quot;xmlRecord&quot;</span><span> </span><span>:</span><span> </span><span>&quot;&lt;person&gt;&lt;name&gt;Jimmy&lt;/name&gt;&lt;/person&gt;&quot;</span></span><span><span>}</span></span></code></pre></div></div>\n<p>Here’s the associated definition:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>import</span><span> com</span><span>.</span><span>orbitalhq</span><span>.</span><span>formats</span><span>.</span><span>Xml</span></span><span><span>model</span><span> </span><span>MyMessage</span><span> </span><span>{</span></span><span><span>    messageId</span><span> </span><span>:</span><span> MessageId</span><span> </span><span>inherits</span><span> </span><span>String</span></span><span><span>    xmlRecord</span><span> </span><span>:</span><span> Person</span></span><span><span>}</span></span><span>\n</span><span><span>@</span><span>Xml</span></span><span><span>model</span><span> </span><span>Person</span><span> </span><span>{</span></span><span><span>    name</span><span> </span><span>:</span><span> PersonName</span><span> </span><span>inherits</span><span> </span><span>String</span></span><span><span>}</span></span></code></pre></div></div>\n<h3 id=\"using-jwt-claims-within-a-query\"><a href=\"#using-jwt-claims-within-a-query\">​<span></span></a>Using Jwt claims within a query</h3>\n<p>You can now pass the claims from a JWT token into Orbital so the claims can be used during query execution,\nand passed to upstream services.</p>\n<p>Let’s look at a simple example where your JWT token has a key piece of information - the customers <code>accountId</code>.\nIf you want to use these to connect with external REST services to run specific Orbital queries, the first thing to do is create a model that\nbuilds on Orbital’s basic <code>JwtClaim</code> model:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>// Custom JwtClaim model with contactId and accountId</span></span><span><span>model</span><span> </span><span>CompanyXJwtClaim</span><span> </span><span>inherits</span><span> </span><span>JwtClaim</span><span> </span><span>{</span></span><span><span>  accountId</span><span>:</span><span> AccountId</span><span> </span><span>// a claim present with the key accountId</span></span><span><span>  </span><span>// other claims omitted</span></span><span><span>}</span></span></code></pre></div></div>\n<p>Adding this model to your system makes sure that <code>accountId</code> is available for Orbital when it runs queries.</p>\n<p>For example, a service may require the users <code>accountId</code> from the JWT be passed in the URL:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>// A service needs to use the current users accountId in the URL: </span></span><span><span>service</span><span> </span><span>AccountService</span><span> </span><span>{</span></span><span><span>   </span><span>@</span><span>HttpOperation</span><span>(</span><span>method </span><span>=</span><span> </span><span>&quot;GET&quot;</span><span>,</span><span>url </span><span>=</span><span> </span><span>&quot;/account/{accountId}&quot;</span><span>)</span></span><span><span>   </span><span>operation</span><span> </span><span>getByAccountId</span><span>(</span><span>@</span><span>PathVariable</span><span>(</span><span>&quot;accountId&quot;</span><span>)</span><span> accountId</span><span>:</span><span> AccountId</span><span>)</span><span>:</span><span> Account</span></span><span><span>}</span></span></code></pre></div></div>\n<p>To make the users <code>accountId</code> available, we simply expose the JWT to the query:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>query</span><span> </span><span>MyQuery</span><span> </span><span>(</span><span> jwt</span><span>:</span><span>CompanyXJwtClaim</span><span> </span><span>)</span><span> </span><span>{</span></span><span><span>   </span><span>given</span><span> </span><span>{</span><span> jwt </span><span>}</span></span><span><span>   </span><span>find</span><span> </span><span>{</span><span> Account</span><span> </span><span>}</span></span><span><span>}</span></span></code></pre></div></div>\n<p>That’s it! The presented JWT is read and the <code>accountId</code> is used to invoice the <code>getByAccountId</code> operation.</p>\n<h3 id=\"errors-on-streaming-messages\"><a href=\"#errors-on-streaming-messages\">​<span></span></a>Errors on streaming messages</h3>\n<p>Previously, it was hard to see when things went wrong inside streaming queries, requiring tailing logs to debug issues.</p>\n<p>Now, errors in streams are shown in a dedicated UI.</p>\n<figure><img src=\"https://orbitalhq.com/assets/query-problems-panel-B4zmnzDG.gif\" alt=\"Errors in streaming queries are now shown off for the beautiful learning opportunities they are.\"/><span></span><figcaption><span>Errors in streaming queries are now shown off for the beautiful learning opportunities they are.</span></figcaption></figure>\n<h2 id=\"usability\"><a href=\"#usability\">​<span></span></a>Usability</h2>\n<h3 id=\"filtering-in-the-results-table\"><a href=\"#filtering-in-the-results-table\">​<span></span></a>Filtering in the results table</h3>\n<p>Results returned from the server can be filtered client side in the query view:</p>\n<figure><img src=\"https://orbitalhq.com/assets/filters-BDk6t8Vl.gif\" alt=\"Filtering tables in query results\"/><span></span><figcaption><span>Filtering tables in query results</span></figcaption></figure>\n<h2 id=\"language-features\"><a href=\"#language-features\">​<span></span></a>Language features</h2>\n<h3 id=\"type-checker\"><a href=\"#type-checker\">​<span></span></a>Type checker</h3>\n<p>We’ve enabled Taxi’s type checker by default, to help surface errors in queries earlier.</p>\n<p>For example, everyone in our office has made this mistake before:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> Film</span><span>[</span><span>]</span><span> </span><span>}</span><span> </span><span>as</span><span> </span><span>{</span></span><span><span>   cast</span><span>:</span><span> </span><span>(</span><span>Actor</span><span>[</span><span>]</span><span>)</span><span> </span><span>-</span><span>&gt;</span><span> </span><span>{</span><span> </span><span>// because this is projecting an array...</span></span><span><span>     name</span><span> </span><span>:</span><span> ActorName</span></span><span><span>   </span><span>}</span><span> </span><span>// &lt;--- ...this should be an array</span></span><span><span>}</span><span> </span><span>// &lt;-- so should this</span></span></code></pre></div></div>\n<p>Now, the compiler tells us what a mistake it was, so we can take a good long look at ourselves.</p>\n<h3 id=\"scoped-variables\"><a href=\"#scoped-variables\">​<span></span></a>Scoped Variables</h3>\n<p>When projecting, you sometimes need to hoist multiple variables into the scope. This is to make them available at the top level of a projection, without declaring them directly in the output.</p>\n<p>For example, in the below example, we want to operate on a single item from an array of values, as well as the parent object.</p>\n<p>However, we need both the objects in scope:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> Film</span><span> </span><span>}</span><span> </span><span>as</span><span> </span><span>(</span><span>Film</span><span>,</span><span> </span><span>first</span><span>(</span><span>Actor</span><span>[</span><span>]</span><span>)</span><span>)</span><span> </span><span>-</span><span>&gt;</span><span> </span><span>{</span><span>  </span><span>// two values declared in scope</span></span><span><span>    title</span><span> </span><span>:</span><span> Title</span><span> </span><span>// title comes from Film</span></span><span><span>    starring</span><span> </span><span>:</span><span> ActorName</span><span> </span><span>// ActorName comes from Actor</span></span><span><span>}</span></span></code></pre></div></div>\n<p>Our query syntax now supports adding these additional variables into the query scope</p>\n<h3 id=\"compiler-warnings-when-something-should-be-declared-closed\"><a href=\"#compiler-warnings-when-something-should-be-declared-closed\">​<span></span></a>Compiler warnings when something should be declared <code>closed</code></h3>\n<p>Marking a model as <code>closed</code> in TaxiQL indicates that the query engine should not attempt to build an instance if you ask for it.</p>\n<p>Generally, this is the expected behaviour for things that a server returns.</p>\n<p>For example:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>// I want the Film object as returned from the server, not constructed</span></span><span><span>model</span><span> </span><span>Film</span><span> </span><span>{</span></span><span><span>   title</span><span> </span><span>:</span><span> Title</span><span> </span><span>inherits</span><span> </span><span>String</span></span><span><span>}</span></span><span>\n</span><span><span>service</span><span> </span><span>Films</span><span> </span><span>{</span></span><span><span>   </span><span>operation</span><span> </span><span>findAllFilms</span><span>(</span><span>)</span><span>:</span><span>Film</span><span>[</span><span>]</span></span><span><span>}</span></span></code></pre></div></div>\n<p>If writing a query like:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>find</span><span> </span><span>{</span><span> Film</span><span>[</span><span>]</span><span> </span><span>}</span></span></code></pre></div></div>\n<p>In the above example, you don’t want Orbital to try to construct a Film instance, you want one returned.</p>\n<p>If you forget to mark <code>Film</code> as <code>closed</code> then the compiler will now warn you.</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>// That&#x27;s better:</span></span><span><span>closed</span><span> </span><span>model</span><span> </span><span>Film</span><span> </span><span>{</span></span><span><span>   title</span><span> </span><span>:</span><span> Title</span><span> </span><span>inherits</span><span> </span><span>String</span></span><span><span>}</span></span></code></pre></div></div>\n<h2 id=\"fixes\"><a href=\"#fixes\">​<span></span></a>Fixes</h2>\n<p>Here’s a big long list of things that we showed to people who asked why we hadn’t been to the pub this month.</p>\n<p>To play us out, while you read through our bug list, here’s the Full House theme song.</p>\n<iframe src=\"https://www.youtube.com/embed/2EL65KLdEHE?si=_o36iLeKjkh63BUS\" title=\"YouTube video player\" frameBorder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowFullScreen=\"\"></iframe>\n<ul role=\"list\">\n<li><code>given</code> clause with cast statement referencing parameter throws exception</li>\n<li>Server sends all responses as arrays</li>\n<li>Server sends arrays when response isn’t an array</li>\n<li>Response arrays of single item are incorrectly unwrapped</li>\n<li>Compiler not erroring when projecting array to non-array</li>\n<li>Can’t use params in call to table operation</li>\n<li>Live query view in history is broken</li>\n<li>Removing a project results in an orphaned view for a project that no longer exists</li>\n<li>Can’t onboard Kafka connections via UI</li>\n<li>Viewing Operations in Services view is borked</li>\n<li>Incorrect responsive layout in TypeViewer component</li>\n<li>Fix streaming results progress bar and record count</li>\n<li>Query History should not list active queries in the history panel, only in the “active” panel</li>\n<li>Copy as Curl breaks with single / double quote issues</li>\n<li>“undefined” entries showing in catalog</li>\n<li>Error not thrown when inner-projection mismatch on array properties</li>\n<li>Schema view shows all types in uppercase</li>\n<li>Duplicates listed in the intellisense</li>\n<li>Active queries in history is missing icons</li>\n<li>Docs missing from catalog page</li>\n<li>Tables in connection details need a minimum width</li>\n<li>Make checkboxes in app-model-attribute-tree-list readonly</li>\n<li>Disable project selector when adding connection</li>\n<li>Inlay type hints not shown in raw results</li>\n<li>Can’t authenticate to Confluent Cloud Kafka</li>\n<li>Format JSON responses in Response not working</li>\n<li>Ensure UI is in correct state when query is run</li>\n<li>Query History takes too long to load</li>\n<li>Projections with fields that are subtypes assign incorrectly</li>\n<li>Fields on anonymous types are not considered for population of graph queries</li>\n<li>Projection of a scoped top-level array fails</li>\n<li>Lineage for cached items can’t trace further back than the cache query</li>\n<li>Query Plan diagram is broken when cache is invoeld</li>\n<li>Services diagram renders shared attributes incorrectly</li>\n<li>StateStore is applied without the annotation</li>\n<li>Streaming queries are rewritten to join streams incorrectly</li>\n<li>Better UX when nothing is registered</li>\n<li>Starting a query with Apple+Enter / Ctrl+Enter shows incorrect time in ticker</li>\n<li>Http 204 responses are not persisted</li>\n<li>Language Server incorrectly reports some types as not defined</li>\n<li>connections.conf not resolving env variables from env.conf</li>\n<li>After saving a query with a streaming union type, can’t rerun another adhoc query with the same streaming union type</li>\n<li>SQL queries fail if the table name is different from the model name</li>\n<li>Profiler view doesn’t show all interactions</li>\n</ul><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2024-03-08-release-announcement-0-30-0\">0.30 is Out - SQL Server, new authentication methods, and JSON inside XML inside a... duck?</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2024-03-08-release-announcement-0-30-0",
            "title": "0.30 is Out - SQL Server, new authentication methods, and JSON inside XML inside a... duck?",
            "summary": "Orbital 0.30.0 - \"Full House\" - MSSQL, SAML, mTLS and Mixed Messages",
            "image": "https://orbitalhq.com/assets/0-30-title-card-BWp82QFu.jpg",
            "date_modified": "2024-03-08T00:00:00.000Z",
            "date_published": "2024-03-08T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2024-01-27-release-announcement-0-27-0",
            "content_html": "<figure><img src=\"https://orbitalhq.com/assets/title-card-i06M7AW0.jpg\" alt=\"\"/><span></span></figure><h2 id=\"orbital-0270---the-cheers-edition-\"><a href=\"#orbital-0270---the-cheers-edition-\">​<span></span></a>Orbital 0.27.0 - The “Cheers” Edition 🍻</h2>\n<p>As the Orbital office farewells Dry January, we welcome back Friday drinks with a fresh release - <a href=\"https://hub.docker.com/layers/orbitalhq/orbital/0.27.0/images/sha256-80b561bf9e936a044ee26b6f8758af41e39d4eb7dc073b6eb7a8c78eabd7162d?context=explore\" target=\"_blank\" rel=\"noreferrer\">0.27.0</a>.</p>\n<p>Pull up a stool, grab your favorite beverage, and let’s toast to the new features, usability improvements, and fixes that are making Orbital 0.27.0 as cozy and welcoming as your favorite Boston bar.</p>\n<h2 id=\"features\"><a href=\"#features\">​<span></span></a>Features</h2>\n<h3 id=\"retry-operations\"><a href=\"#retry-operations\">​<span></span></a>Retry Operations</h3>\n<p>Like Sam Malone behind the bar - or a Fraser reboot - we know the importance of second and third chances.</p>\n<p>That’s why we’ve introduced <code>@Retry</code> annotations, ensuring that if at first you don’t succeed, requests can try, try again to connect and get your data flowing just right.\nRead more about it in the release announcement <a href=\"https://orbitalhq.com/changelog/2024-01-26-retry-operations\">here</a>, and the docs are available <a href=\"https://orbitalhq.com/docs/describing-data-sources/http#retry-policies\">here</a></p>\n<h3 id=\"cache-eviction\"><a href=\"#cache-eviction\">​<span></span></a>Cache Eviction</h3>\n<p>There’s nothing worse than overstaying your welcome when it’s time to leave.</p>\n<p>Orbital’s caches now support Cache headers, evicting data when it’s stale.\nRead more about it <a href=\"https://orbitalhq.com/changelog/2024-01-26-cache-eviction\">here</a>, with the docs available <a href=\"https://orbitalhq.com/docs/querying/caching\">here</a></p>\n<h3 id=\"better-connections-for-kafka\"><a href=\"#better-connections-for-kafka\">​<span></span></a>Better Connections for Kafka</h3>\n<p>Our Kafka connections now have more options than Frasier had patients - with full support for secure Kafka connections.\nExpect a seamless experience that keeps the data pouring in.  More detail available in our <a href=\"https://orbitalhq.com/docs/describing-data-sources/configuring-connections#additional-kafka-connection-properties\">docs</a></p>\n<h2 id=\"usability-stuff\"><a href=\"#usability-stuff\">​<span></span></a>Usability Stuff</h2>\n<h3 id=\"better-handling-of-query-compilation-errors\"><a href=\"#better-handling-of-query-compilation-errors\">​<span></span></a>Better handling of query compilation errors</h3>\n<p>Pobody’s Nerfect, right?</p>\n<p>Orbital now displays compilation errors in a slightly less antagonistic way. Read more <a href=\"./2024-01-25-better-query-compilation-errors\">here</a>.</p>\n<h3 id=\"query-history-refreshes\"><a href=\"#query-history-refreshes\">​<span></span></a>Query history refreshes</h3>\n<p>Running a new query now refreshes the history view automatically, much like Norm Peterson’s beer mug. You’ll always have the latest information at your fingertips, no manual refresh needed.</p>\n<h3 id=\"loading-bars\"><a href=\"#loading-bars\">​<span></span></a>Loading bars</h3>\n<p>Our loading bars have received a makeover, becoming more informative and visually appealing. It’s the little details that make all the difference, ensuring you’re not left hanging while waiting for your data to appear.</p>\n<h2 id=\"fixes\"><a href=\"#fixes\">​<span></span></a>Fixes</h2>\n<ul role=\"list\">\n<li>\n<p><strong>Projections of Nested Arrays</strong>: We’ve fixed the projection of nested arrays, ensuring that your data structures are handled with the care and precision of Cliff Clavin’s factoids—accurate, detailed, and meticulously organized.</p>\n</li>\n<li>\n<p><strong>Nested Values in Discovery:</strong> Nested values are now correctly used as inputs to discovery, making your data exploration as smooth and effective as Woody Boyd’s charm. No more confusion or missteps.</p>\n</li>\n<li>\n<p><strong>Cancelled Queries:</strong> Cancelled queries now appropriately show as “cancelled,” clearing up any confusion and ensuring you’re not left wondering what went wrong. It’s straightforward communication, just how Coach would have wanted it.</p>\n</li>\n<li>\n<p><strong>Improved Performance of Loading Query History:</strong> We’ve supercharged the performance of loading your query history, making it faster than ever. It’s like getting your data served up by Sam himself—quick, efficient, and with a smile.</p>\n</li>\n<li>\n<p><strong>Fix duplicates on child properties with same names:</strong> An unfortunate little gremlin meant in certain conditions child properties with the same name as a parent would get incorrect values.</p>\n</li>\n</ul>\n<p>So, here’s to Orbital 0.27.0 — Cheers to faster data, better performance, and a community where everyone is connected! 🍻</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2024-01-27-release-announcement-0-27-0\">0.27 is Out - Retry, Cache Eviction and UX improvements</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2024-01-27-release-announcement-0-27-0",
            "title": "0.27 is Out - Retry, Cache Eviction and UX improvements",
            "summary": "Orbital 0.27.0 - The \"Cheers\" Edition - Release Notes",
            "image": "https://orbitalhq.com/assets/title-card-i06M7AW0.jpg",
            "date_modified": "2024-01-27T00:00:00.000Z",
            "date_published": "2024-01-27T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2024-01-26-cache-eviction",
            "content_html": "<figure><img src=\"https://orbitalhq.com/assets/cover-image-CjJkw9qI.png\" alt=\"\"/><span></span></figure><h2 id=\"expiry-for-cached-queries\"><a href=\"#expiry-for-cached-queries\">​<span></span></a>Expiry for Cached Queries</h2>\n<p>Orbital now honours HTTP cache headers on HTTP responses, when a cache is enabled for a query.</p>\n<p>As described in <a href=\"https://orbitalhq.com/docs/querying/caching\">Caching</a> Orbital provides ability to cache query results with <code>@Cache</code> annotation:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>@</span><span>Cache</span><span>(</span><span>connection </span><span>=</span><span> </span><span>&quot;mycache&quot;</span><span>)</span></span><span><span>find</span><span> </span><span>{</span><span> Film</span><span>[</span><span>]</span><span> </span><span>}</span></span></code></pre></div></div>\n<p>Let assume that you have a rest end point returning list of films:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>service</span><span> </span><span>FilmServices</span><span> </span><span>{</span></span><span><span>  </span><span>@</span><span>HttpOperation</span><span>(</span><span>method </span><span>=</span><span> </span><span>&quot;GET&quot;</span><span>,</span><span> url </span><span>=</span><span> </span><span>&quot;...&quot;</span><span>)</span></span><span><span>  </span><span>operation</span><span> </span><span>listFilms</span><span>(</span><span>)</span><span>:</span><span>Film</span><span>[</span><span>]</span></span><span><span>}</span></span></code></pre></div></div>\n<p>If your Rest operation returns <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control\" target=\"_blank\" rel=\"noreferrer\">Cache-Control</a> header in its response,\nOrbital will take the <code>max-age</code> directive into account and expire the cached query result accordingly.</p>\n<p>Here as an example response with Cache-Control header:</p>\n<figure><img src=\"https://orbitalhq.com/assets/http-response-cache-control-wDWMvd05.png\" alt=\"\"/><span></span></figure>\n<p><code>max-age=144004</code> will get Orbital to cache the Film list for 4 hours. After 4 hours, the items in the query cache will expire automatically.</p>\n<h2 id=\"grab-the-docs\"><a href=\"#grab-the-docs\">​<span></span></a>Grab the docs</h2>\n<p>Read more about caching in our <a href=\"https://orbitalhq.com/docs/querying/caching\">docs</a></p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2024-01-26-cache-eviction\">Cache Eviction support</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2024-01-26-cache-eviction",
            "title": "Cache Eviction support",
            "summary": "Orbital's caching layer now supports HTTP Cache headers",
            "image": "https://orbitalhq.com/assets/cover-image-CjJkw9qI.png",
            "date_modified": "2024-01-26T00:00:00.000Z",
            "date_published": "2024-01-26T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2024-01-26-retry-operations",
            "content_html": "<figure><img src=\"https://orbitalhq.com/assets/cover-image-xte7Vc4K.jpg\" alt=\"\"/><span></span></figure><h2 id=\"retry-option-for-http-operations\"><a href=\"#retry-option-for-http-operations\">​<span></span></a>Retry Option for Http operations:</h2>\n<p>Let’s say you need to stitch Film data with reviews coming from an unreliable third party Rest Api:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>service</span><span> </span><span>ReviewsApi</span><span> </span><span>{</span></span><span><span>    </span><span>@</span><span>HttpOperation</span><span>(</span><span>method </span><span>=</span><span> </span><span>&quot;GET&quot;</span><span>,</span><span> url </span><span>=</span><span> </span><span>&quot;https://reviews/{id}&quot;</span><span>)</span></span><span><span>    </span><span>operation</span><span> </span><span>getReviews</span><span>(</span><span>id</span><span>:</span><span> FilmId</span><span>)</span><span>:</span><span> FilmReview</span><span>[</span><span>]</span></span><span><span>}</span></span></code></pre></div></div>\n<p>The third party REST API gives you intermittent HTTP 500s as it can’t cope with user requests at particular times during day.</p>\n<p>To address such outages, you can now define a <code>HttpRetry</code> settings in Taxi for the operation.</p>\n<p>Orbital Supports two <code>HttpRetry</code> policies:</p>\n<h3 id=\"retry-policy-with-fixed-delay\"><a href=\"#retry-policy-with-fixed-delay\">​<span></span></a>Retry Policy with Fixed Delay</h3>\n<p>Let say you want to Orbital to retry ‘getReviews’ operation when it responds with Http 500. You want Orbital to try the operation at most 10 times and wait 5 seconds between each retry.\nHere is how you can modify the taxi definition:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>service</span><span> </span><span>ReviewsApi</span><span> </span><span>{</span></span><span><span>    </span><span>@</span><span>HttpOperation</span><span>(</span><span>method </span><span>=</span><span> </span><span>&quot;GET&quot;</span><span>,</span><span> url </span><span>=</span><span> </span><span>&quot;https://reviews/{id}&quot;</span><span>)</span></span><span><span>    </span><span>@</span><span>HttpRetry</span><span>(</span><span>responseCode </span><span>=</span><span> </span><span>[</span><span>500</span><span>]</span><span>,</span><span> fixedRetryPolicy </span><span>=</span><span> </span><span>@</span><span>HttpFixedRetryPolicy</span><span>(</span><span>maxRetries </span><span>=</span><span> </span><span>10</span><span>,</span><span> retryDelay </span><span>=</span><span> </span><span>5</span><span>)</span><span>)</span></span><span><span>    </span><span>operation</span><span> </span><span>getReviews</span><span>(</span><span>id</span><span>:</span><span> FilmId</span><span>)</span><span>:</span><span> FilmReview</span><span>[</span><span>]</span></span><span><span>}</span></span></code></pre></div></div>\n<p>You can trigger the retry mechanism for multiple response codes as well, here is how you can modify previous Taxi definition to trigger the Retry mechanism when the  remote service returns either Http 500 or Http 502</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>service</span><span> </span><span>ReviewsApi</span><span> </span><span>{</span></span><span><span>    </span><span>@</span><span>HttpOperation</span><span>(</span><span>method </span><span>=</span><span> </span><span>&quot;GET&quot;</span><span>,</span><span> url </span><span>=</span><span> </span><span>&quot;https://reviews/{id}&quot;</span><span>)</span></span><span><span>    </span><span>@</span><span>HttpRetry</span><span>(</span><span>responseCode </span><span>=</span><span> </span><span>[</span><span>500</span><span>,</span><span> </span><span>502</span><span>]</span><span>,</span><span> </span></span><span><span>      fixedRetryPolicy </span><span>=</span><span> </span><span>@</span><span>HttpFixedRetryPolicy</span><span>(</span></span><span><span>        maxRetries </span><span>=</span><span> </span><span>10</span><span>,</span><span> </span></span><span><span>        retryDelay </span><span>=</span><span> </span><span>5</span></span><span><span>    </span><span>)</span><span>)</span></span><span><span>    </span><span>operation</span><span> </span><span>getReviews</span><span>(</span><span>id</span><span>:</span><span> FilmId</span><span>)</span><span>:</span><span> FilmReview</span><span>[</span><span>]</span></span><span><span>}</span></span></code></pre></div></div>\n<h3 id=\"retry-policy-with-exponential-delay\"><a href=\"#retry-policy-with-exponential-delay\">​<span></span></a>Retry Policy with Exponential Delay</h3>\n<p>With exponential delay retry policy, Orbital will wait progressively longer intervals between consecutive retries:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>service</span><span> </span><span>ReviewsApi</span><span> </span><span>{</span></span><span><span>    </span><span>@</span><span>HttpOperation</span><span>(</span><span>method </span><span>=</span><span> </span><span>&quot;GET&quot;</span><span>,</span><span> url </span><span>=</span><span> </span><span>&quot;https://reviews/{id}&quot;</span><span>)</span></span><span><span>     </span><span>@</span><span>HttpRetry</span><span>(</span><span>responseCode </span><span>=</span><span> </span><span>[</span><span>500</span><span>,</span><span> </span><span>502</span><span>]</span><span>,</span><span> fixedRetryPolicy </span><span>=</span><span> </span><span>@</span><span>HttpExponentialRetryPolicy</span><span>(</span><span>maxRetries </span><span>=</span><span> </span><span>10</span><span>,</span><span> retryDelay </span><span>=</span><span> </span><span>5</span><span>,</span><span> jitter </span><span>=</span><span> </span><span>0.5</span><span>)</span><span>)</span></span><span><span>    </span><span>operation</span><span> </span><span>getReviews</span><span>(</span><span>id</span><span>:</span><span> FilmId</span><span>)</span><span>:</span><span> FilmReview</span><span>[</span><span>]</span></span><span><span>}</span></span></code></pre></div></div>\n<p>With the above definition, Orbital will try invoking the Rest at most 10 times in case of Http 500 or Http 502 errors.</p>\n<p>However, the delay between retries will increase exponentially.</p>\n<p>With a <code>jitter</code> value between 0 and 1, you add some randomness to the delay time by introducing a random delay, or “jitter”, to the next retry delay time.\nThis ensures that the retries are not synchronous and reduces the likelihood of a retry storm.</p>\n<p>Here’s how the table would look with exponential backoff with Jitter:</p>\n<p>| Retry Attempt | Delay Time (seconds)  | \tJitter Range (seconds) | Actual Delay Time (seconds) |\n| ------------- | ---- -----------------|------------------------- |-----------------------------|\n| 1             | 1.0                   | 0.5                      | 1-0 - 1.5                   |\n| 2             | 2.0                   | 0.5                      | 1-5 - 2.5                   |\n| 3             | 4.0                   | 0.5                      | 3.5 – 4.5                   |\n| 4             | 8.0                   | 0.5                      | 7.5 – 8.5                   |\n| 5             | 16.0                  | 0.5                      | 15.5 – 16.5                 |</p>\n<h2 id=\"summary\"><a href=\"#summary\">​<span></span></a>Summary</h2>\n<p>Http retries is out in 0.27. Read more about it over in our <a href=\"https://orbitalhq.com/docs/describing-data-sources/http#retry-policies\">docs</a></p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2024-01-26-retry-operations\">Http Retry Support</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2024-01-26-retry-operations",
            "title": "Http Retry Support",
            "summary": "If at first you don't succeed",
            "image": "https://orbitalhq.com/assets/cover-image-xte7Vc4K.jpg",
            "date_modified": "2024-01-26T00:00:00.000Z",
            "date_published": "2024-01-26T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2024-01-25-better-query-compilation-errors",
            "content_html": "<figure><img src=\"https://orbitalhq.com/assets/query-compilation-error-CQhcC4Tl.gif\" alt=\"\"/><span></span></figure><p>Some UI/UX love for Orbital now, with a fine tuning of how compilation errors are being displayed on screen.</p>\n<p>We’ll continue to improve the surfacing of messages like this throughout Orbital, allowing a more streamlined experience for users.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2024-01-25-better-query-compilation-errors\">Better query compilation errors</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2024-01-25-better-query-compilation-errors",
            "title": "Better query compilation errors",
            "summary": "description goes here",
            "image": "https://orbitalhq.com/assets/query-compilation-error-CQhcC4Tl.gif",
            "date_modified": "2024-01-25T00:00:00.000Z",
            "date_published": "2024-01-25T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2024-01-15-m2-images",
            "content_html": "<figure><img src=\"https://orbitalhq.com/assets/cover-image-C7euE211.png\" alt=\"\"/><span></span></figure><p>Some users have been experiencing errors when running our docker images on\nApple Silicon Macbook Pro’s (M1-3 et al).</p>\n<p>The issue appears to be related to the base image we use for our Docker images - <code>eclipse-temurin:alpine</code>.</p>\n<p>To address this, we’ve started producing extra images, based on Ubuntu Jammy.</p>\n<p>Grab the images with the <code>-jammy</code> suffix, eg:</p>\n<ul role=\"list\">\n<li><code>0.27.0-jammy</code></li>\n<li><code>next-jammy</code></li>\n</ul>\n<p>These images aren’t suitable for production usage, as the <code>Ubuntu Jammy</code> base image contains\nsignificantly more security CVE’s than the <code>-alpine</code> images.</p>\n<p>However, for local developer environments, they’re great - mainly because they don’t crash. Brilliant.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2024-01-15-m2-images\">Apple Silicon Friendly docker images available</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2024-01-15-m2-images",
            "title": "Apple Silicon Friendly docker images available",
            "summary": "New docker images for M2 Macbooks",
            "image": "https://orbitalhq.com/assets/cover-image-C7euE211.png",
            "date_modified": "2024-01-15T00:00:00.000Z",
            "date_published": "2024-01-15T00:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2024-01-05-http-headers-date-math",
            "content_html": "<h2 id=\"http-headers\"><a href=\"#http-headers\">​<span></span></a>Http Headers</h2>\n<p>The <code>@HttpHeader</code> annotation now is now supported, for setting headers on HTTP requests.</p>\n<p>For example:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>service</span><span> </span><span>MyService</span><span> </span><span>{</span></span><span><span>  </span><span>@</span><span>HttpOperation</span><span>(</span><span>method </span><span>=</span><span> </span><span>&quot;GET&quot;</span><span>,</span><span> url </span><span>=</span><span> </span><span>&quot;...&quot;</span><span>)</span></span><span><span> </span></span><span><span>  </span><span>// A fixed, static header</span></span><span><span>  </span><span>@</span><span>HttpHeader</span><span>(</span><span>name </span><span>=</span><span> </span><span>&quot;Consumes&quot;</span><span>,</span><span> value </span><span>=</span><span> </span><span>&quot;application/json&quot;</span><span>)</span></span><span><span>  </span><span>operation</span><span> </span><span>findPeople</span><span>(</span><span>)</span><span>:</span><span>Person</span><span>[</span><span>]</span></span><span>\n</span><span><span>  </span><span>// A header that&#x27;s calculated</span></span><span><span>  </span><span>operation</span><span> </span><span>findPeople</span><span>(</span></span><span><span>     </span><span>@</span><span>HttpHeader</span><span>(</span><span>name </span><span>=</span><span> </span><span>&quot;If-Modified-Since&quot;</span><span>)</span><span> ifModifiedSince</span><span> </span><span>:</span><span> Instant</span><span> </span><span>=</span><span> </span><span>addDays</span><span>(</span><span>now</span><span>(</span><span>)</span><span>,</span><span>-</span><span>7</span><span>)</span></span><span><span>  </span><span>)</span><span>:</span><span>Person</span><span>[</span><span>]</span></span><span><span>}</span></span></code></pre></div></div>\n<p>You can also pass header values in using the <code>given {}</code> clause in a query:</p>\n<div><div><span></span><span>Copy</span></div><div><pre><code><span><span>service</span><span> </span><span>MyService</span><span> </span><span>{</span></span><span><span>  </span><span>operation</span><span> </span><span>findPeople</span><span>(</span></span><span><span>     </span><span>@</span><span>HttpHeader</span><span>(</span><span>name </span><span>=</span><span> </span><span>&quot;Cache-Control&quot;</span><span>,</span><span> prefix </span><span>=</span><span> </span><span>&quot;max-age=&quot;</span><span>)</span><span> cacheMaxAge</span><span> </span><span>:</span><span> CacheMaxAge</span></span><span><span>  </span><span>)</span><span>:</span><span>Person</span><span>[</span><span>]</span></span><span><span>}</span></span><span>\n</span><span><span>// which is then invoked as:</span></span><span><span>given</span><span> </span><span>{</span><span> maxAge</span><span> </span><span>:</span><span> CacheMaxAge</span><span> </span><span>=</span><span> </span><span>64000</span><span> </span><span>}</span><span> </span></span><span><span>find</span><span> </span><span>{</span><span> Person</span><span>[</span><span>]</span><span> </span><span>}</span></span></code></pre></div></div>\n<p>Read more about it in our <a href=\"https://orbitalhq.com/docs/describing-data-sources/http#http-headers\">docs</a></p>\n<h2 id=\"date-math\"><a href=\"#date-math\">​<span></span></a>Date math</h2>\n<p>The stdlib in Taxi has been expanded to add date math functions to the stdlib:</p>\n<ul role=\"list\">\n<li><code>addMinutes</code></li>\n<li><code>addDays</code></li>\n<li><code>addSeconds</code></li>\n<li><code>now</code></li>\n<li><code>parseDate</code></li>\n</ul>\n<p>Read more about these in the <a href=\"https://taxilang.org/language-reference/stdlib/#dates\" target=\"_blank\" rel=\"noreferrer\">Taxi docs</a></p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2024-01-05-http-headers-date-math\">Http Headers and Date Math</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2024-01-05-http-headers-date-math",
            "title": "Http Headers and Date Math",
            "summary": "describption goes here",
            "image": "https://orbitalhq.com/changelog-cards/2024-01-05-http-headers-date-math.png",
            "date_modified": "2024-01-05T01:00:00.000Z",
            "date_published": "2024-01-05T01:00:00.000Z"
        },
        {
            "id": "https://orbitalhq.com/changelog/2024-01-05-auth-with-cognito-azure",
            "content_html": "<figure><img src=\"https://orbitalhq.com/assets/changelog-cognito-auth-BtruFZU7.png\" alt=\"\"/><span></span></figure><p>Orbital now supports authentication and authorization with <a href=\"https://orbitalhq.com/docs/deploying/authentication#azure\">Azure AD (Entra)</a> and <a href=\"https://orbitalhq.com/docs/deploying/authentication#aws-cognito\">AWS Cognito</a></p>\n<p>This expands our OpenId Connect auth implementation (we already support <a href=\"https://orbitalhq.com/docs/deploying/authentication#keycloak\">Keycloak</a>).</p>\n<p>Full documentation with vendor-specific tutorials is now available in our sections on <a href=\"https://orbitalhq.com/docs/deploying/authentication\">Authentication</a> and <a href=\"https://orbitalhq.com/docs/deploying/authorization\">Authorization</a>.</p><p><em>(The post <a href=\"https://orbitalhq.com/changelog/2024-01-05-auth-with-cognito-azure\">Auth with AWS and Azure</a> appeared first on <a href=\"https://orbitalhq.com/changelog\">Orbital Changelog</a>.)</em></p>",
            "url": "https://orbitalhq.com/changelog/2024-01-05-auth-with-cognito-azure",
            "title": "Auth with AWS and Azure",
            "summary": "describption goes here",
            "image": "https://orbitalhq.com/assets/changelog-cognito-auth-BtruFZU7.png",
            "date_modified": "2024-01-05T00:00:00.000Z",
            "date_published": "2024-01-05T00:00:00.000Z"
        }
    ]
}