Platform shells, command-line interface, and testing

Fission is suitable for production macOS, Windows, Linux, Web, Android, iOS, Terminal, Static site, and SSR work today. The clearest way to understand that claim is to separate three different questions that often get mixed together.
The first question is whether the shared app model is ready for real product work. In Fission, that answer is yes. Your app state, actions, reducers, widgets, layout rules, semantics, and rendering model live in one shared runtime that is meant to power real macOS, Windows, Linux, Web, Android, iOS, Terminal, Static site, and SSR targets.
The second question is whether every shell wrapper and every testing convenience is equally mature today. That answer is more nuanced. The shells expose slightly different wrapper methods, and the current live-testing hooks are not identical on every target.
The third question is whether a specific target still needs direct validation. That answer is always yes, because serious cross-platform work always has host-specific behavior to prove. Browser packaging, mobile safe areas, soft keyboards, accessibility bridges, terminal cell constraints, static route output, SSR cache policy, and simulator versus device differences are real host concerns even when the shared runtime is stable.
Keeping those three questions separate makes the rest of the page easier to read. Tooling gaps do not mean the app model is not ready. Host validation needs do not mean one platform is the only real target. They simply mean cross-platform work still has a real platform boundary.

What a shell is in Fission

A shell is the thin platform-facing layer that boots the shared runtime inside a real host.
On macOS, Windows, and Linux, that means a native window. On Web, it means a browser entrypoint and browser renderer. On Android and iOS, it means the mobile host that packages, launches, and presents the same shared app on that platform. For Terminal, it means a terminal cell renderer. For Static site, it means a build-time shell that renders Fission widget output into semantic HTML. For SSR, it means a server process that renders routes at request time or through cache policy.
The shell is not where your product architecture should live. Your reducers, selectors, widgets, and explicit side effects stay in shared Rust code. The shell's job is narrower: it starts the app, forwards input, presents frames, connects platform services such as clipboard and accessibility, and exposes the host-specific hooks needed for launch and validation.
That boundary is one of the reasons Fission can aim for strong cross-platform parity. The framework keeps the meaning of the interface in the shared runtime and keeps platform differences near the host edge where they belong.

Shells available today

Fission now has shell families that map onto the public target set.
| Shell family | Targets | Use it when | Main API | | --- | --- | --- | | Desktop | macOS, Windows, Linux | You want a native window. | DesktopApp | | Web | Web | You want a browser app compiled to WebAssembly. | WebApp | | Mobile | Android, iOS | You want mobile hosts for the same shared app. | MobileApp | | Terminal | Terminal | You want an interactive terminal user interface. | TerminalApp | | Static site | Static site | You want generated HTML routes from Fission widgets and Markdown content. | FissionSite and fission site ... | | SSR | SSR | You want request-time HTML, cache revalidation, server jobs, signed actions, workers, or islands. | FissionServerApp and fission server ... | | Test driver | Supported live hosts | You want automated shell control for supported live hosts. | fission-test and fission-test-driver |
Desktop, web, and mobile are runtime shells: they keep the app running in a host event loop. The terminal shell is also a runtime shell, but its renderer targets terminal cells instead of a GPU-backed window. The Static site shell is build-time output: it lowers real Fission widgets to Core IR and emits static HTML, CSS, and site assets. The SSR shell is request-time output: it renders routes on demand, drains server jobs, manages route cache policy, verifies signed actions, and serves generated workers or islands when a page needs focused browser behavior.
Those differences are host differences, not app-model differences. The same state, reducers, selectors, widgets, design system, and environment model stay at the center.

The shared runtime is the same, even when wrappers differ

When you choose DesktopApp, WebApp, MobileApp, or TerminalApp, you are not choosing a different application model. You are choosing which shell wrapper should boot the same runtime for the host you care about.
These runtime wrappers revolve around the same product questions. What is the initial state? Should the app dispatch a startup action? Which values should be synchronized into Env each frame? Which jobs, services, or capabilities should the host know how to run outside pure reducers?
That shared shape is the important part. It means the same mental model travels with you across platforms.
The public wrappers are not completely symmetrical, so it helps to understand the differences by purpose rather than memorizing a method inventory.
DesktopApp currently exposes the broadest public setup API. It can replace the initial environment with with_env(...), it exposes reducer-registration helpers directly, and it can open the live test-control port with with_test_control_port(...). That makes it a very convenient host for local iteration and automated live testing.
MobileApp exposes the same core runtime ideas: state setup, startup action flow, environment synchronization, async registration, and live test-control support. It also includes the Android-specific run_with_android_app(...) host entrypoint used when the Android shell hands control to Fission.
WebApp keeps the browser wrapper intentionally small. It supports the same shared startup and async-registration story, but it does not currently expose a public live test-control port or a public key-handler hook. That should be read as a shell-tooling difference in the current browser wrapper, not as a statement that the browser target is less real. A web app still runs the same shared runtime and can still be a production app.
TerminalApp runs the same build, lower, layout, verify, and render pipeline inside a terminal. It is useful for command tools, dashboards, setup flows, and developer workflows where the user already lives in a terminal. It verifies terminal compatibility from Core IR and semantics rather than from hard-coded widget names. Continue to Terminal user interfaces for a full guide.
FissionSite is the static site shell. It is not an interactive app loop. It renders configured routes at conversion time, turns Markdown and custom Fission page widgets into semantic HTML, writes shared CSS, generates search data when enabled, and copies configured assets. Continue to Static sites for the site workflow.
FissionServerApp is the SSR shell. It runs as an HTTP process, renders Fission routes per request or cache policy, drains server jobs before responding, signs server actions, and can attach progressive workers or focused WASM islands to pages that need browser-side behavior. Continue to SSR guide for the server workflow.

How developers actually use shell wrappers

A good rule is to use the wrapper for host startup concerns and keep product behavior in the shared app.
with_state_init(...) is for synchronous state preparation before the first frame. with_startup_action(...) is for entering the normal reducer flow as soon as the runtime is ready. with_sync_env(...) is for values that belong in Env, such as theme, locale, translation bundles, or other read-only context widgets should consume during component conversion.
with_async(...) is where you register jobs, services, and capabilities that the host may need to run outside pure reducers. with_frame_hook(...) exists for narrow polling or wakeup cases, but it should stay a host-level helper instead of becoming your primary application architecture.
The practical lesson is simple: the shell should prepare the platform around your app, not become the place where your app logic drifts.

What the command-line interface is for

The command-line interface exists so you do not have to hand-assemble those host layers yourself.
When you run:
fission init my-app
you get the shared app files, the initial desktop entrypoint, the fission.toml manifest, and the baseline project structure.
When you later run:
fission add-target web ios android --project-dir my-app
you are not cloning your product into separate codebases. You are generating additional host wrappers for the same shared app.
That is the right way to think about the command-line interface. It creates and extends the host boundary. It does not change the core architecture of your app.
A generated host project is simply the platform-specific wrapper around the shared runtime. The command-line interface is the normal front door for that wrapper: fission devices shows where the app can run, fission run --target <target> builds and launches it, fission logs reconnects to supported log streams, and fission test --target <target> runs the generated smoke test. The lower-level scripts remain checked in under platforms/<target>/ for continuous integration and advanced debugging, but day-to-day development should not require memorizing platform-specific script names.
On the web, that means building the WebAssembly output, serving it locally, and opening the browser when requested. On Android and iOS, that means packaging, installing, launching, and attaching to the device or simulator logs by default.

Readiness, tooling, and target caveats are not the same thing

This is the most important distinction on the page.
Fission's cross-platform production story rests on the shared runtime and real host paths that already exist across the public target set. That is the framework-readiness claim.
Testing-tooling differences are a different category. For example, desktop and mobile currently expose with_test_control_port(...), which opens a live shell-control endpoint over transmission control protocol (TCP), the standard reliable transport used for local network connections. The browser wrapper does not yet expose that same live-control path publicly. That is a limitation in the current browser testing API. It is not evidence that browser apps built with Fission are not viable.
Target caveats are a third category. Browser clipboard behavior, drag-and-drop details, and input method editor edge cases still need direct browser validation. Mobile touch handling, safe-area behavior, soft-keyboard behavior, and some mobile lifecycle integration still deserve direct Android and iOS validation. Terminal layout must be checked against terminal cells and supported interaction modes. Static site output must be checked as static HTML. SSR output must be checked under request, cache, action, worker, and island behavior. Those caveats are about proving host-specific behavior where it actually lives.
When those categories stay separate, the platform story becomes much clearer. The shared runtime is ready to build real apps. Tooling around that runtime still has room to improve on some targets. Specific targets still need the same kind of platform-aware testing any serious app would need.

What differs by target in practice

Desktop is a strong host for fast iteration because it offers native windows, a short local run loop, and the richest current live-testing API. That makes it convenient, but convenience is not the same thing as legitimacy.
Web runs through the browser shell and WebAssembly, which is the compiled format that lets Rust run in the browser. The checked-in web shell, browser smoke example, and generated browser target are real host paths today. The current missing browser live-control hook is a testing-tooling gap. Richer browser integration around clipboard, drag-and-drop, and input method editor handling is a host integration area to validate carefully. Neither point changes the fact that the browser target itself is real.
Android runs through generated or checked-in mobile hosts and currently has a verified emulator path. Android development adds the usual host setup, including the Android software development kit (SDK) for platform tooling and the Android native development kit (NDK) for the native toolchain used by this Rust-based path. Those are normal Android-host concerns, not signs that the shared runtime is less serious there.
iOS also runs through a real host path and currently has a verified simulator flow in the repository. The simulator can fall back to a software renderer when CoreSimulator cannot satisfy the normal graphics path, which keeps the simulator useful for development and validation. That is a current host-environment detail, not a downgrade of the platform story. As with any production iOS app, simulator runs and device validation answer different questions.
Terminal runs through the terminal shell. It uses the same app model, but output must be compatible with terminal cells, keyboard-first interaction, bounded scrollback, and the capabilities of the terminal emulator the user actually runs.
Static site runs through the site shell. It is not an app event loop; it is a target for crawlable HTML, CSS, metadata, search assets, sitemap output, robots output, and copied assets generated from Fission routes and content.
SSR runs through the server shell. It is the target for request-time HTML, sessions, signed actions, jobs, route cache policy, progressive workers, and focused browser islands.

A practical validation strategy that treats every target seriously

A good validation strategy starts from the behavior you are trying to prove.
If you are testing reducer logic, selector logic, view derivation, layout intent, semantics, and other shared runtime behavior, lean hard on deterministic tests. They are fast, repeatable, and reflect Fission's architecture well.
If you are testing host interaction through a real shell, use live shell testing where the wrapper supports it today. Desktop and mobile currently provide the strongest public live-control path.
If you are testing browser packaging, browser viewport behavior, clipboard rules, drag-and-drop, accessibility bridges, mobile safe areas, soft keyboards, simulator launch, emulator installation, or device-specific presentation, validate in that actual host. That is not a fallback after “real development.” That is the correct place to prove host behavior.
So the practical strategy is not "desktop first, everyone else later." It is "use the fastest tool for shared behavior, and use the real host whenever the host is part of the question."

What to keep in mind as you ship

Fission's public promise is one shared app model across macOS, Windows, Linux, Web, Android, iOS, Terminal, Static site, and SSR targets. The implementation supports that promise now. What varies by platform is the surrounding shell tooling, packaging path, and host-specific validation work, not whether the framework's core model applies.
That is exactly the boundary a serious cross-platform framework should draw. The runtime stays shared. The shells stay thin. Tooling can keep improving without changing the truth of the app model underneath.

Where to go next

For the broader testing strategy, continue to Testing and diagnostics. For the beginner guide to examples, targets, and when to branch into host validation, read Examples and targets. For the first-run setup flow, go back to Quickstart.

What you should have working

After this guide, you should be able to explain where Platform shells, command-line interface, and testing fits in the Fission lifecycle, identify the file or component you changed, run the relevant fission command, and verify the result in a real target or test.

Common mistakes to check

Symptom
What to check first
The app compiles but nothing changes
Confirm the component is actually used by the route or shell target you are running.
An action fires but state does not update
Confirm the reducer is registered with the same action value that the UI dispatches.
A platform feature reports unsupported
Confirm the target has the capability in fission.toml and the shell registered the provider.
The page works on one target but not another
Run fission doctor, then inspect target-specific configuration and feature flags.
Tests are hard to write
Split the product rule into a reducer test first, then add a UI or shell smoke test for the integration.
Fission
A cross-platform, GPU-accelerated user interface framework for Rust. MIT licensed.
Copyright (c) 2026 Fission
Ready to use today. Widget APIs are expected to remain stable; some runtime and shell APIs may change before 1.0.0.
Fission 0.7.0