features

June Update

Solutions, standalone apps, migration tooling, and the platform hardening around 1.0.

By Jack Musick

Last month I said Chat V2 was the near-term focus. That absolutely did not happen, but I think what shipped was more useful (and more fun).

This spans v0.9.1 through v1.0.7 and crosses the arbitrary 1.0 line. The repo and container images moved to the gobifrost org (mostly to enable an org-locked GitHub feature so merges stop being slower than they need to be), and releases now have a clearer ladder: :dev for every merge, release candidates for soak testing, and full releases for :latest. Mostly process, but it matters once other people are running this.

Solutions

Bifrost now has an installable unit for apps, workflows, forms, agents, tables, configs, events, schedules, and files. A Solution can own what it deploys, make those resources read-only in the target environment, and update them through deploys instead of letting production drift one admin-screen edit at a time.

I keep coming back to Power Platform as the comparison here. Power Platform got a lot right with Solutions, Dataverse, and connection references: package the app, declare what it depends on, let the installer wire environment-specific connections. Bifrost needed that same shape, but with local development, git, Python workflows, agents, and real code as first-class pieces instead of something bolted on after the fact. It also solved a lot of problems I have where the platform acted just different enough to confuse coding agents, so it forced me to think harder about local development and what a modern Git-based workflow should look like. It also means that future things that land in the Bifrost Community Workspace can come in as installable, portable artifacts rather than just a mess of things to have Claude migrate for you.

Connection references and setup. A Solution can declare the integrations it needs. Install surfaces missing connections, blocks or flags incomplete setup, and gives the operator a checklist instead of a runtime ModuleNotFoundError or a workflow that fails the first time it tries to call an API.

Export and backups. Exports are now job-backed and file-aware. Larger exports can run asynchronously, include selected file payloads, and come back as a downloadable bundle when the worker is done. That is not flashy, but it is the difference between “this works in a demo” and “I can move this somewhere else.”

Round trips. Deploys carry source artifacts and serialize apps, workflows, configs, policy refs, events, schedules, tables, and files more reliably. That sounds like bookkeeping until you try to install the same thing in a second environment and discover the copy is not actually the same thing.

Solution setup wizard

Apps And Migration

Apps got a real v2 model. New apps are standalone React projects that import the Bifrost SDK as a package served by the platform instead of leaning on injected globals from the host shell. From Claude’s perspective, app development now looks like normal React, and the app can run locally while proxying workflows to your local workspace. This removes the need for a draft model, and means apps can look and work as well as the latest models and coding agents can manage instead of fighting platform weirdness.

The table web SDK had already landed before this release window, but standalone v2 made it part of the actual app package: tables.*, useTable, and useInfiniteTable for reads, writes, filters, and live updates. v1.0.5 added the missing file side with files.* and useFiles, including uploads, downloads, signed URLs, metadata-aware listing, and policy-gated browser access.

Migration got attention too, because “just rebuild it in v2” is not a plan.

bifrost:migrate. There is now a migration skill for moving a legacy inline app and its backing workflows, tables, configs, and other loose pieces into a clean v2 Solution. The important part is not that it automates everything. It does not, and should not. The important part is that it separates deterministic work from judgment.

bifrost solution migrate-app. The CLI handles the deterministic part: scaffold a standalone v2 app, port v1 source files, rewrite the old from "bifrost" import surface into real React, router, lucide, shadcn, and SDK imports, install the shadcn pieces the app actually uses, and then stop with a checklist. If a v1 app used hooks that do not exist in v2, or workflow UUID refs that need portable path::function refs, the tool should tell you instead of inventing an answer.

Capture dry-runs. The migration flow uses Solution capture dry-runs to find the dependency closure and outside references. That answers the question I care about before moving something: who else uses this? If a workflow is shared by two apps, the tool surfaces that as a decision instead of yanking it into one Solution and breaking the other app.

The plugin itself got cleaned up around this. The Bifrost skills are packaged for both Claude and Codex now, and the build skill understands the Solution workspace model, v2 web SDK, Solution files, and the _repo versus Solution split. There is also a Copilot Cowork packaging skill that can turn a Bifrost agent into a Microsoft 365 Copilot Cowork plugin package. Early, but pointed in the right direction.

Access And Events

The access model moved forward in a few places.

External users and everyone. authenticated no longer means every signed-in person. External users, like portal guests, are excluded unless an entity is explicitly opened to the new everyone tier or granted through a role. That matters once customer-facing surfaces and internal tools live in the same system.

Custom claims. Before this, table policies could check fixed user facts like role, org, or user id. They could not cleanly say “this user can read documents for any campus they belong to” unless you duplicated that logic somewhere else. Now you can define an allowed_campus_ids claim that queries a membership table for the current user, then write a document policy like { in: [{ row: campus_id }, { claims: allowed_campus_ids }] }. Add allowed_doc_type_ids the same way and the policy can require both. Messy per-user access becomes data plus a policy.

Custom claims list

Users and roles. Magic-link invites landed, with passkey, password, or SSO registration on a branded accept page. Roles now cover apps, workflows, and knowledge too, with a redesigned Roles page that shows where each role is used.

Topic events. Workflows can emit topic events through the SDK, and subscribed workflows receive context.event. Platform events exist now too, including workflow failures, integration lifecycle changes, and delivery retry exhaustion. This replaced the old built-in email module, which was convenient but the wrong boundary. Events are a better primitive.

Per-mapping OAuth connections also landed. Each org’s integration mapping can connect independently, with token lookup cascading from mapping to org to global. Again, very Power Platform in spirit: the package carries the reference, the environment supplies the connection.

Hardening

A lot of the release window was less about new surfaces and more about making the surfaces survive real use.

The execution sandbox is tighter. Worker pods run with a hardened security context, and execution children do not inherit platform credentials. Requirements install is more resilient too: one unbuildable package no longer strips the entire workflow runtime, and the real pip error shows up in the UI.

Process pools got fixed after some ugly edges showed up. Race-prone cleanup paths could crash executions or leave new work waiting for slots, and a previous worker reaping change had to be rolled back after it caused a production crash storm. Not pretty, but useful.

Diagnostics now report cgroup working set instead of memory.current, which was roughly doubling memory usage in the UI and firing the 85% admission gate too early. Import cleanup also cut idle memory across workers, the scheduler, API, and template processes without changing the deployment shape.

Diagnostics memory chart

The client got a large UI pass: rebuilt history, better execution details, coalesced tracebacks, copyable errors, a pretty output renderer, a cleaner dashboard, and a rebuilt command palette. When something runs, fails, or looks suspicious, the platform should help you understand it instead of making you spelunk through raw traces.

Local Development

The CLI now has a contract gate, so an outdated CLI blocks instead of quietly talking to a server with a different idea of the world. Org targeting is more consistent across commands, and bifrost files can read, write, list, delete, check existence, and search through the Python files SDK.

The latest patch release tightened the Solution workflow: bifrost solution create creates the remote install, writes bifrost.solution.yaml, and binds local .env; solution bind attaches a cloned workspace to an existing install; solution start now requires that binding and passes the Solution and org context through local data-plane calls. Local development should stay attached to the install you meant, not the shared workspace by accident.

Windows onboarding got a practical pass too: native PowerShell initialization, RabbitMQ fixes for newer Docker behavior, UTF-8 CLI stdio, and POSIX repo keys so watch mode stops re-uploading every file because of path separators. I still prefer WSL for serious local work, but Windows should not be a trapdoor.

Smaller Fixes Worth Naming

v0.9.1 fixed current OpenAI model testing by using max_completion_tokens, and fixed Gemini MCP tool registration for array parameters. Thanks to @mlspivey for the Gemini compatibility fix in #225.

v1.0.6 fixed a scarier CLI regression where sync, watch, and push could treat a failed server file listing as “everything is new.” Auth, permission, and network failures are now fatal. That is the correct kind of unfriendly.

Thanks to @MTG-Thomas for switching the self-host object-storage examples to SeaweedFS in #229 and normalizing WebAuthn passkey transports in #357. Thanks to @sdc53 for fixing OAuth authorize URL construction when a template already has query parameters in #288.

What’s next

The immediate goal is to drive Solutions through real customer work. We have a few projects in flight, and I expect the next round of improvements to come from actually trying to build, migrate, package, install, and update things instead of staring at the architecture and declaring it elegant.

Chat V2 is still on the radar, but it is lower priority than it was last month. Everyone already has a chat subscription, and replacing Copilot or Claude for everyday use is probably not realistic. The part I still care about is the tooling story. Connecting MCP servers is still a chore almost everywhere, and I want a chat surface where the knowledge, workflows, integrations, and permissions we have already built are just there. I do not know yet whether that is worth giving up provider-native things like Work IQ. I have not figured out how to have both.

The bigger thought is isolation. Solutions make it easier to imagine a managed place where internal users, external users, or customers can build small tools with access to the integrations they are allowed to touch, without IT inheriting a random local app that nobody knows how to host, secure, or support. The current version of “let users build with AI” often turns into someone asking IT to bless an unknown stack on 127.0.0.1 that might be PHP, Python, .NET, Node, Docker, not Docker, password-protected, or absolutely not password-protected. Seems bad.

If Bifrost can become the managed sandbox for that work, the pitch changes. Build here. Use these integrations. Stay inside these boundaries. Ship it as a Solution when it grows up. That feels like a much better problem to support.