I've been working on Patchouli X for a while now and I think it is time to start refactoring about the architecture.
This issue essentially serves as a working document on the direction that I hope Patchouli X will achieve. In addition,
it might help others who are doing something similar.
It is important to note that Patchouli X is a project developed by a single person.
In the beginning...
I've deliberated for quite a while about the general tech stack that Patchouli X will use. After many considerations, I
landed on using Web technologies because of the following:
It is what I'm the most familiar with.
Typescript is a good programming language and JavaScript has a rich ecosystem.
I like "typed functional programming" languages like OCaml and Haskell, so I find myself quite happy with the
expressive type system that TypeScript brings despite its "faults" which I can live with.
While JavaScript has its woes (which I don't think I need to deliberate much here), with the help of TypeScript, it
is a good language as it doesn't restrict "expression" (i.e. you're not forced to do it the "JavaScript" way,
unlike Java/C# where the whole ecosystem pushes you to a specific expression).
It is a marketing material, so it is very biased, but I feel the same way.
Higher chance of a version of Patchouli X still working in the future despite halted development.
You can still visit old websites from the 90s and most functionalities are still working.
Other technologies like Java doesn't have a good track record (i.e. try running older versions of Minecraft on
newer versions of Java).
Even though you can run them on older versions of Java, you need to have someone who maintains legacy Java
versions for current platforms/architectures (ex. Java 8 on Apple Sillicon/AWS Gravitron/OpenBSD/NetBSD/FreeBSD).
In addition, of the Desktop version, we need a Mobile and WebExtension version of Patchouli.
I don't want to use multiple languages.
Like speaking natural languages, you have to "flush the cache" in your brain everytime you switch.
With all of that in mind, I started building Patchouli X with React, tRPC, and Electron. The reason for Electron is
simply because it is the established "go-to" and I thought that I would have no problems with it.
Current state of affairs
Patchouli X has a "working version". It is not feature complete and it is definitely not up to the "level" in terms of
user experience that I like.
I have been experiencing roadblocks from using Electron.
Heavy IO workloads freezes the main process which freezes the renderer process
I'm quite surprised when I discovered this because I assumed the renderer and main processes are completely
separated, but it appears there's enough connection between the two that the main process cannot be in a "frozen"
state or the frontend will be unusable.
While electron-vite has helped a lot in taming the situation, it is still not great
especially the packaging part.
We cannot adopt Monorepo nor do node_modules differently because Electron needs a fully hoisted node_modules
which will be copy-pasted into the app.asar
I learned why Electron projects has the frontend dependencies as devDependencies the hard way.
I want to adopt Monorepo to have clear separation between the frontend and the backend.
The development setup is quite awkward as you need to start the entire application every time you change the
backend but the frontend can be hot-reloaded.
Electron is too bounded to a specific version of both Chromium and Node.js.
It tries its best to bridge the gap between the two worlds, but it doesn't feel great.
Might as well just explicitly separate these two worlds.
And a lot of things are canned because I couldn't get it to work.
Path to sandboxing is unclear
The future at 10'000 ft
I want to achieve the following high level architecture for Patchouli X.
flowchart TB
subgraph Backend
Scraper --- Router
Database --- Router
Scraper -.- Database
end
subgraph Desktop
Router -- tRPC over stdout/stdin --- Main
Main --- Window1["Window"]
Main --- Window2["Window"]
end
subgraph WebExtension
Router -- tRPC over ??? --- Extension
end
This is not by any means the final end product, but it's an illustration of what I'd like to achieve.
In the backend, each module is responsible for few things and has proper sandboxing which only grants whatever it
needs and nothing more.
This is akin to the philosophy taken by OpenBSD with pledge(2) and unveil(2).
Backend only has enough permissions to do IPC/RPC.
Database only has read-write access to the Patchouli Vault and IPC.
Scraper only has a subset networking to fetch web pages/assets and IPC.
Separate backend from the desktop client allows headless/daemon mode.
This reduces memory footprint which would've been occupied by the desktop client.
In addition, we should allow Patchouli to be used from various clients (i.e. WebExtension, CLI).
Development would be less awkward as the backend is separate from the desktop client.
Desktop frontend can still be hot-reloaded, while the backend will hot-restart.
Desktop and Backend communication will be over tRPC over HTTP which allows easier introspection via DevTools.
At ground level
Part 1: Divorce the Backend and Frontend
The first step would be to migrate towards Monorepo structure and separate the backend and the frontend completely.
We will throw away Electron for now and just run Patchouli in a discrete web browser.
In detail, we need to:
Throw away Electron.
Migrate to Monorepo.
Separate the backend and the frontend.
Eliminate electron-vite and move to vanilla React/Vite/TS setup.
Figure out a way to bundle/build the backend and use ESM (likely with Vite as well).
Setup development environment
Run backend as discrete daemon with hot-restart.
Run frontend as discrete daemon with HMR/hot-reload.
Part 2: Shipping a Desktop version
The second step is to build a desktop application from the backend/frontend.
After much deliberation, I think Tauri is a good choice because:
It's not just a glue between Node.js/Chromium like Electron, it is also involved in building/installing/distribution
applications.
This was a major annoyance with Electron as this is deferred to third-party projects like Forge/Electron Builder as
mentioned before.
Having an official way to make a Windows installer, a macOS bundle, and a Linux package is a huge step-up.
Not bounded to a specific version of Chromium and use the installed "WebView provider" that is shipped with the system.
We don't have to worry about shipping urgent security updates whenever a major security vulnerability is found in
Chromium.
Should reduce memory footprint, but it is unclear whether this is true.
Sidecar is a first-party feature.
Tauri doesn't force you to make the "backend" in the main process.
Shipping extra binaries to run a separate backend is a supported feature.
However, it is unclear how the sidecar binary will be built, especially when it comes to what runtime to use.
Tauri's example uses Vercel's pkg to generate a monolithic binary
I've been working on Patchouli X for a while now and I think it is time to start refactoring about the architecture.
This issue essentially serves as a working document on the direction that I hope Patchouli X will achieve. In addition, it might help others who are doing something similar.
It is important to note that Patchouli X is a project developed by a single person.
In the beginning...
I've deliberated for quite a while about the general tech stack that Patchouli X will use. After many considerations, I landed on using Web technologies because of the following:
With all of that in mind, I started building Patchouli X with React, tRPC, and Electron. The reason for Electron is simply because it is the established "go-to" and I thought that I would have no problems with it.
Current state of affairs
Patchouli X has a "working version". It is not feature complete and it is definitely not up to the "level" in terms of user experience that I like.
I have been experiencing roadblocks from using Electron.
node_modules
differently because Electron needs a fully hoistednode_modules
which will be copy-pasted into theapp.asar
devDependencies
the hard way.The future at 10'000 ft
I want to achieve the following high level architecture for Patchouli X.
This is not by any means the final end product, but it's an illustration of what I'd like to achieve.
pledge(2)
andunveil(2)
.At ground level
Part 1: Divorce the Backend and Frontend
The first step would be to migrate towards Monorepo structure and separate the backend and the frontend completely. We will throw away Electron for now and just run Patchouli in a discrete web browser.
In detail, we need to:
Part 2: Shipping a Desktop version
The second step is to build a desktop application from the backend/frontend.
After much deliberation, I think Tauri is a good choice because:
However, it is unclear how the sidecar binary will be built, especially when it comes to what runtime to use.
Part 3: ???
Part 4: Profit!