Closed autodidaddict closed 1 year ago
I've confirmed that the Flutter app option would not require extra work to enable NATS client functionality. That's available today and works out of the box from a github open source project that exposes NATS via both TCP and WebSockets depending on which platform is the target.
Decision: TLDR deprecate the phoenix washboard for something that will work with our many different wasmCloud hosts.
Requirements notes from our wasmCloud community meeting:
wash ui
and that will bring up whatever the new washboard will be.My $0.02:
Background:
Given the above, I rank the proposed options in this order: 1) React bundled with Tauri (not Yew): small bundles, fast performance. It does require potential contributors to maybe touch a little Rust from time to time. I'd like to see a POC or thoughts from someone who has experience with Tauri before feeling totally comfortable with this, but it's where I'm leaning. 2) React bundled with Electron: lets UI contributors stay in JS/TS-land, and Electron is always Chrome, so contributors wouldn't need to worry as much about cross-platform rendering issues 3) React embedded in Rust manually: painful; I don't really see an upside over Tauri 4) React bundled with Flutter, only if we can address the concerns about unsupported NATS clients 5) Phoenix app: it's probably the lowest effort, but the team is getting burnt out on distributing OTP applications. I think we could make this work, but I'm not confident there's enough appetite to put in more work to make releasing and distributing seamless
I have a strong concern about depending on a Dart client. I don't see an "official" one in the NATS org, and while I did find one linked to from NATS' blog, it hasn't been updated since 2019. We've run into limitations and frustrations wrt. an Elixir NATS client, and I'd rather pick a client that is well-supported
There's a dart library for NATS that is more maintained than the one you found, it received an update a few months ago: https://github.com/chartchuo/dart-nats
React bundled with Flutter, only if we can address the concerns about unsupported NATS clients
This isn't really possible. Flutter isn't a backend, Flutter is roughly analogous to React/Reactive Native. You would choose React or Flutter, not both.
I'd like to take Electron off the list of possibilities. It's a terrible developer experience and no one in the universe has ever said, "oh great, this is an Electron app". There are way more viable options, Electron should be a last resort if all else fails.
I think we can also probably take the Phoenix app off the list. I don't think anyone on the team wants to manage another OTP deployment. This would boil your list down to:
I didn't realize all the code in Flutter would be Dart. Requiring contributors to learn another new language and framework is too high a burden at this stage, in my opinion
Would it be possible to have it be an app running on the wasmcloud instance itself, deployed via wadm?
It could still be started with a wash command that abstracts that away, but that might allow avoiding the need for a desktop app as well as any special infrastructure or learning - working on washboard would be working on a normal wasmcloud app.
One of the concerns I personally have with a any of the desktop app options (dart, tauri, electron, etc.) is for developing in desktop-less environments, like within a dev container, on codespaces or gitpod, as well as allowing access via non-desktop devices like mobile.
I kind of like the concept making it be a normal wasmcloud actor. The big problem with doing it as an actor though is you still eventually have to have something that talks to NATS. Using an actor to bundle up and serve the assets isn't out of the question though as a distribution mechanism, regardless of most of the options here. It looks like at a minimum any of the ~tauri~/yew/react options get us a web page that could be accessed from any device
Overall though, I am definitely in favor of decoupling the washboard from the host no matter which technical direction we choose to take
Given the:
Perhaps Dioxus is a technology worth considering? https://dioxuslabs.com/
Took a look at Dioxus. It would require the team to learn a new framework (dioxus) but not a new language. It took me about 3 minutes total from the docs page to a running hello world app. That said, Dioxus's desktop support is similar to electron...
The desktop is a powerful target for Dioxus but is currently limited in capability when compared to the Web platform. Currently, desktop apps are rendered with the platform's WebView library, but your Rust code is running natively on a native thread
This isn't necessarily a negative against this framework, but it's worth pointing out the differences in that it uses a hosted web view while Flutter can build real native targets without the web view "trick".
I did some experimentation with tauri
and, quite honestly, I was blown away. I originally thought Tauri would just start a local web server and then when you start the rust binary, you just then open a web browser to the port and you're interacting with a React app.
What I ended up building was something far more like what I wanted from Flutter
. The create-tauri-app
rust cargo plugin is sweet, and it took care of all the things I loathe and despise about setting up React apps. I was then able to use cargo tauri build
to create a native macOS DMG installer. The starter app looks like this:
This is the entirety of the Rust app:
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
And here's the React code:
function App() {
const [greetMsg, setGreetMsg] = useState("");
const [name, setName] = useState("");
async function greet() {
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
setGreetMsg(await invoke("greet", { name }));
}
return (
<div className="container">
<h1>Welcome to Tauri!</h1>
<div className="row">
<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" className="logo vite" alt="Vite logo" />
</a>
<a href="https://tauri.app" target="_blank">
<img src="/tauri.svg" className="logo tauri" alt="Tauri logo" />
</a>
<a href="https://reactjs.org" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<p>Click on the Tauri, Vite, and React logos to learn more.</p>
<div className="row">
<form
onSubmit={(e) => {
e.preventDefault();
greet();
}}
>
<input
id="greet-input"
onChange={(e) => setName(e.currentTarget.value)}
placeholder="Enter a name..."
/>
<button type="submit">Greet</button>
</form>
</div>
<p>{greetMsg}</p>
</div>
);
}
export default App;
I felt at ease and fully at home in this environment, and the development cycle (once all the build caches are created!) feels like it might actually be fun. It does force the developer to use both React and Rust, and it makes it a bit harder to have someone build "just the front-end", but I think that's an easily enough conquered obstacle.
The pattern of having the JS invoke the Rust backend with a "less typed" wrapper feels a little awkward, but given that the Rust app would be able to natively use the lattice control client crate, any of the wash crates, the wasmcloud runtime crate if necessary, it feels like a reasonable price to pay.
In light of this, I'd like to move that we start with a Tauri + React application for washboard. If we discover that Tauri isn't usable, the worst case scenario is that we'll have a React app that we can migrate to another bundler.
I've used Tauri, Dioxus, and Leptos for fun earlier this year and agree with the Tauri recommendation at this point. Two other benefits to Tauri:
Main con I saw with Tauri was final binary size, but I think that's improving.
I wonder though how many people really use the Washboard interface to spin stuff up after their first month of Wasmcloud use. It's helpful when you're first starting and later to visualize what you already have setup, but my guess is most people move on to wash or wadm after they get their feet wet. If that's true, it might be easier to treat the Washboard as a read only tool by default and just display the state of the host in something like Grafana since it's pretty common and probably already part of a wasmcloud Dev toolkit to leverage tracing with OTEL.
It also gives you a bunch of connectivity options out of the box:
Biggest pro to me would being able to get the high level status and tracing in one spot. If we went the datasource plugin route it would also make it relatively easy to maintain for the core team, while leaving end users flexibility on the visualization.
Major cons are the AGPL-3.0 license (might be pro for some), and read only aspect by default. Although there are also plenty of tricks to allow Grafana to drive/configure over http as well if it helps first time users.
FYI a pointer to HN discussion on a great comparison of UI libs: Emerging Rust GUI Libraries in a Wasm World.
Take anything I say with a grain of salt. My ultimate goal is for Wasmcloud to rock and bring the most value to the industry. I do not gain anything from being right.
TL;DR: React/NextJS/Tauri (probably with Tailwind and radix-ui)
I have almost 9+ years in React and 6+ in Elixir. When LiveView came out, I didn't adopt it for one reason that has nothing to do with technicality: culture, despite Elixir being what makes me smile the most, pure joy and love for the language.
Far too often, I see tools coming and going in my long journey. This time may not be the same. I am still optimistic about it.
The idea of "one language rules them all" keeps missing the target by ignoring the cultural impact of that statement.
JavaScript could be better, and all those GUI tools have their problems. Still, the reality is that the friction in the frontend stack using JavaScript is far less than anything compared to it, especially now with tools like Vercel and Vite for deploying them (not applicable here; I am not focusing on deployment for wasmcloud).
Most backend folks are less passionate about the GUI topic, in my experience, and most frontend people will hang out closer to the JavaScript community and even designers. Hiring/Recruiting/Finding Contributors becomes even more problematic. You must find that person who likes to do both things or find the frontend person who sometimes needs to learn more about the backend concerns beyond a simple HTML/Web server that renders HTML. You left out a massive community that could contribute today due to the popularity of those tools.
At the same time, you have passionate people doing their best to strive for perfection in their ecosystem. It is a lost situation in the long term, in my opinion, based on my own experience of watching the same situation repeat itself (Keycloak Admin Dashboard rewrite is my favorite example of it)
Nowadays, I am a bit pessimistic about "one language rules them all," I would instead focus on leveraging whatever each ecosystem and community has to offer. Far too often, conversations about performance and "the best language" drive the conversations.
That being said, there is a long-term investment to make because it may be the case that today, the Rust devs would prefer not to learn something new. 🤷🏻
From the technical side,
Backend-first languages want to own the frontend stack. They get far enough to create a basic admin dashboard with low user expectations. One fundamental question I love to ask to see how mature the tools are to focus on is, "How do you do animations," I am not talking about basic CSS animations but more advanced situations where you would leverage React Framer Motion (as an example, pick any other UI framework). Having to rediscover the same problems as others did and ending up understanding that either the browser gives you control (which APIs like Page Transition help) or you must bring the data closest to the UI, whether you like it or not.
For Rust/Backend-first devs, if you feel the frontend stack sometimes is too complex, it is because it is, but due to lack of experience, they are achievable DX where it is pleasing for You need to invest in and avoid the same mistakes as before. I can help. I messed up plenty of time and found "few things" that work well, tested by my passion for delegating to less experienced and Junior devs so they can own things. The good part is that your tool is simple today, so if you have an excellent start, you should be able to scale the codebase without that much chaos.
About Flutter, research about the lack of jobs and whatnot, it probably has its day counted. Again, I don't get paid for being right. I hope it doesn't because it is an excellent tool, but for the same reason, I am saying leverage the JS ecosystem is why I would say ignore the tool.
What would I do in a perfect world if it were purely about technical concerns?
It has been a long journey in React. After so many years, I am tired of dealing with silly problems like dependency arrays and lack of observable design patterns for such cases as the primary example. If you ask me what the most "technical is sounded" tool out there in the market as of today, I would say Vue@v3 (NOT v2) because it was a combination of the best of Angular and React combined into a perfect library, not just that, a community that rock and core maintainers that take ownership for a lot of things that you are left by yourself in React (it does not bother me because I do it over and over and over by now). I can say the same about Svelte.
BUT React, we like it or not, is the most used library out there, and the ecosystem is so massive that concern alone would make me pick React over Vue if I were the CTO. It is not just about the technical topic but the ecosystem, hiring, market ... so many other things. I wish it weren't the case sometimes.
Thanks, @yordis!
I forgot to publish the ADR for this RFC, but what you TLDR'd at the top is almost exactly what we went for. We went for statically compiled assets from Vite over a full-fledged Next.js project for the time being, but that may change based on needs.
You can see the implementation in the wash source and you can run the new UI with the latest build of wash with the following command:
wash ui --experimental
Maybe it is worth sharing the following: https://straw-hat-team.github.io/adr/5541831634/README.html
I applied it successfully on multiple teams. I am biased because of the value that brought me more than anything else.
We've been experimenting with a simple no-backend and react-frontend dashboard for about a month now with https://github.com/wasmCloud/wash/pull/643
@lachieh first demoed this during the 05 Jul 2023 weekly community call: https://www.youtube.com/live/FN9POjvwnZE?feature=shared
We've been able to bundle this without issue and support a set of MVP functionality.
Summary
This request for comment seeks commentary on the idea of extricating the wasmCloud dashboard (aka
washboard
) functionality from the OTP host and ensuring that functionality is held elsewhere. Note that the washboard functionality will not be retired or deleted, only made available on its own without requiring it be embedded in a host.Rationale
The first and probably easiest to identify issue with the status quo is that both the core wasmCloud host (
host_core
) and the Phoenix server that provides the dashboard functionality (wasmcloud_host
) exist in the same distribution. This means that, by default, every deployed version of a wasmCloud host carries with it the extra dependencies, size, and networking port requirements of a full web application.This tight coupling presents problems above and beyond the deployment footprint. In our recent efforts to create static builds, having the Phoenix application be a required piece of the target output makes the pipelines more difficult. This tight coupling also means that every time we want to change, enhance, or fix the
washboard
, we have to release a new version of the host, even though the two logical components should be able to have their own independent release cadences.A fairly unknown and esoteric bit of code in the OTP host actually involves a special internal pub/sub mechanism that compensates for the dashboard being within the same process as a running host. In other words, we're actually writing code to counteract negative consequences of the pairing. There are code paths we actually need to test that only arise from having the dashboard and core host in the same process.
Implementation
This section enumerates a number of possible implementations of the washboard functionality as a separate entity from the host. In all of the below cases, washboard will be removed from the OTP host. This is an objective list with identified, practical pros and cons.
Implicit in the implementation is the addition of support in the washboard UI for interacting with wadm
Embed React App in Rust+Hyper
This implementation option would follow the same pattern we use for things like our petclinic UI. Embed the various bundled UI assets inside a Rust binary, and use a built-in web server like the Hyper crate to serve up the assets. Hyper would then also have to expose an API that the React application would use.
Pros
Cons
Yew and Tauri
Yew is a Rust-based web front end framework that has undergone exponential growth since its original creation. It gives developers a familiar component-based UI framework while also using pure Rust. The Tauri project takes pretty much any front-end (including Yew) and bundles it all up inside an easily distributed Rust binary.
Pros
Cons
Wash WASI Plugin
This option involves sitting on top of wash plugins implemented as WASI components. Wash, which would serve as an ephemeral bespoke host, would provide this plugin with the
lattice-controller
provider suitably configured with a NATS connection, as well as a bound HTTP server provider on a port. The actor component would then respond to HTTP requests and interact with the lattice via the lattice controller provider.Pros
Cons
Phoenix (Elixir) App
This involves extracting the
wasmcloud_host
top-level OTP application, removing the tentacles that require the underlying core host, and building and distributing it as a separate application.Pros
Cons
Flutter App
Flutter is a cross-platform framework that allows you to define your user interface in terms of platform agnostic primitives and it takes care of building the target-native binary, including iOS, Mac, Linux, Windows, and Android. This should not be confused with other bundling tools that take a web application, cram them into a bundle, and then run them inside an iframe in a native browser component.
The flutter app would communicate with NATS over WebSockets
Pros
wash console
Cons
Risks
Typically the biggest risk is time. We could invest a large amount of time in this effort and ultimately not please the wasmCloud community. Otherwise this seems pretty low risk. The idea that the admin dashboard should be decoupled from the actual server daemons is fairly uncontroversial. The only real risk is in being unable to decide on which of the alternatives we want to use.