Orbital 0.39: workspaces, Copilot and data validation

The first 0.39 milestone is out. Run separate schemas in one instance, ask your data questions with Copilot, and add validation rules to your types. Here's what changed, and what to check before upgrading.

Marty Pitt@marty_pitt

Orbital 0.39.1-M1 is out! It’s the first milestone of 0.39, and you can grab the milestone build from the release page.

This is our biggest release so far, with four months of work behind it. You can now run independent schemas in one instance, ask your data questions in plain English, and get Copilot to help write your Taxi projects.

We’ve also added a validation engine and secrets management, and replaced the single-query editor with one that works on whole projects. There’s a lot to try, so here’s a tour of what’s new.

It’s an early-adopter build, so expect some rough edges. There are also breaking changes, including a new license requirement and a query history migration. Read those before upgrading, even if you don’t plan to use the new features.

Workspaces: Run separate schemas in one instance

Until now, an Orbital instance had one schema. If two teams wanted independent sets of types and services, you needed separate instances.

Now you can give each team a workspace in the same instance. Each workspace has its own projects, connections and query history, so you can try a change to a service in one workspace without changing the schema everyone else is querying.

To enable workspaces and organisations:

vyne:  toggles:    workspace-mode: OrgAndWorkspace

You’ll get a selector in the header for switching between them. The catalog, editor and activity view all follow the workspace you’ve selected.

Organisations group workspaces under a tenant. If you’re running Orbital on-prem, you’ll generally only need one organisation; Workspace mode gives you the workspace selector without the organisation UI.

A lot of the work here was making connections, caches, search and history respect those boundaries. Previously, some of that state was shared across the instance, which doesn’t work when two workspaces have different ideas about what a Customer is.

If you’re happy with a single schema, leave workspace-mode at its default, None. You’ll keep the existing routes and won’t see a workspace selector.

Copilot: Ask questions, or get help building a schema

Once you’ve described your APIs and data sources with semantic types, Orbital knows how to fetch data and combine it. Copilot lets you ask for that data in plain English.

For example, ask “What’s Jimmy’s account balance?” in Ask mode. Copilot generates and runs a TaxiQL query against your current workspace, and gives you the answer with the query and a link to its run in query history.

That query is there to inspect and reuse. If the answer looks wrong, you can open the run and see how Orbital got there.

Switch to Build when you want help writing the project itself. Copilot edits your Taxi files, with changes appearing in the editor beside the conversation as it writes. Having the code right there makes this a much more useful place to work through a schema.

You choose Ask or Build when you start a conversation; it stays in that mode.

Copilot runs as a separate service. You’ll need to configure its endpoint and enable it in Orbital; the release notes have the setup.

MCP server: Bring your own agent

There’s also an MCP server, so you can ask about your data from Claude, Cursor or another MCP client. Enable it with:

vyne:  toggles:    mcp-server-enabled: true

Each workspace has an endpoint:

/api/{orgId}/{workspaceId}/mcp

It exposes a single ask tool. The tool description is generated from the workspace’s schema, so the client gets some context about the data available there. Requests use the workspace’s existing organisation-membership checks.

Secrets manager: Keep credentials out of your project config

Previously, configuring credentials meant environment variables or values in config files. Git credentials could end up embedded in a URL in workspace.conf, which is a file you might want to check into git.

You can now store credentials in a secrets manager and reference them from config:

connections.jdbc.warehouse {   url:      "jdbc:postgresql://warehouse.example.com:5432/prod"   password: ${secrets.DB_PASSWORD}}

Orbital resolves the secret when it needs to open the connection, rather than putting the password into the merged config. Git authentication uses secret references too.

We support AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager and Infisical. There’s also an encrypted file backend for local development, but don’t use that in production: the encryption key sits beside the data. The default in-memory backend is for tests and demos.

Secrets can belong to a workspace, an organisation or the server. A workspace’s value takes precedence, so you can use the same DB_PASSWORD reference with different credentials in development and production.

Rotation doesn’t need a restart, but values are cached for 15 minutes by default. If you rotate a secret outside Orbital, use the refresh endpoint to pick it up immediately. See managing secrets for setup and cache configuration.

Validation engine: Put validation rules on your types

If Name shouldn’t be empty, you shouldn’t have to repeat that rule in every API that returns one. You can now declare it on the type:

import com.orbitalhq.validation.NotEmptyimport com.orbitalhq.validation.Valid
@NotEmptytype Name inherits String
@Validmodel Person {   name : Name}

A Person arriving as { "name": "" } now produces a validation error at name. For a find query, the default is to reject it with an HTTP 400.

The @Valid matters. Rules only run inside an activated scope, so adding @NotEmpty to a shared type won’t suddenly break every query that uses it. You can opt models in as you’re ready.

There are twelve built-in rules, covering things like ranges, string lengths, patterns and unique items in collections. The rule reference lists them all.

You also control what happens when a rule fails. For example, this query reports errors but lets the data through:

@OnValidationFailure(action = ValidationAction.Warn, threshold = Severity.Error)query GetOrder {   find { Order }}

Queries can reject, warn or drop an offending record. By default, find rejects at Error severity and stream drops the record at Error; invalid query arguments always reject.

All violations are reported, including those below the action’s severity threshold. So you can start a rule at Warning, see how much existing data fails it, and move it to Error once you’re comfortable enforcing it.

One thing to watch: @Valid also enforces nullability. Previously, you could declare a field non-nullable without Orbital enforcing it. That wasn’t great, and you may discover some unexpected nulls when you enable validation on an existing model.

Collection options: Sort and paginate across your data sources

Collection options ship in this milestone. You can sort, paginate, limit and deduplicate query results, including data from sources other than databases:

find { Person[]( CountryCode == "GB", orderBy: DateOfBirth desc, offset: 20, limit: 10 ) }

Orbital asks the source to do that work when it can, and handles it after fetching when it can’t. We announced collection options in July; this is the first build that includes them. The same goes for database deletes and native SQL queries.

Code editor: Work on the whole project

The new editor gives you a file tree for the whole project, including config files and READMEs. You can arrange your code, query results and history in dockable panels, and the layout stays put when you refresh.

You can also save a file that doesn’t compile. Previously we rejected the save if the package had compilation errors, which made saving unfinished work rather difficult. An ambitious requirement for a Save button.

Database connectors: New Databricks support and a rebuilt Snowflake connector

There’s new Databricks support, and we’ve rebuilt the Snowflake connector. The old Snowflake implementation had its driver commented out of the build, so there wasn’t much you could do with it.

We’ve also fixed database-backed HTTP endpoints returning 200 OK with an empty body when they couldn’t connect. An unreachable database now returns 502; an undefined connection returns 500. Previously, you couldn’t tell a broken connection from a query that found nothing.

Query graph debugger: See why a query won’t resolve

If you’re debugging why Orbital can’t resolve a query, there’s a new query graph debugger too. Paste in the graph from the planner logs and explore the types and connections.

Before you upgrade

The full breaking-change list covers behaviour changes as well as deployment changes. A few to plan for:

  • Orbital now requires a license. Sign in to download and configure a free one automatically. For air-gapped deployments, get in touch.
  • Back up your query history database. The migration runs automatically and takes table locks, so allow for downtime if you have a large history.
  • If you used the old workspaces toggle, replace it with vyne.toggles.workspace-mode. The old key is ignored.
  • H2 is no longer shipped as a data-source driver. Migrate any H2 connections before upgrading.
  • The standalone Jet pipeline server has been removed. If you rely on it, stay on 0.38.x for now. Streaming queries are unaffected.
  • Custom extensions need updating for Spring Boot 4 and Jackson 3.

Workspaces and validation are opt-in, but that doesn’t make the whole release backwards-compatible. Check the notes for changes to numeric comparisons, parameter matching and HTTP response formats too.

Give the milestone a try

If you’ve been waiting to split out a team’s schema, try Copilot against your own data, or put validation around a troublesome service response, you can do that in this build.

Try the milestone somewhere that isn’t production first. As always, come and tell us what’s broken.