The case for using a web browser as your terminal
59 points by pomdtr
59 points by pomdtr
I almost stopped reading at:
The web browser is the ultimate window manager
No, the browser is absolutely awful as a window manager. It is the main reason why I don’t use web apps whenever I can.
But the author made a choice and went all the way. I am impressed.
Honestly, compared to macos built-in window manager, anything is competitive.
Using a browser like arc feels quite close to using a tiling window manager like i3 in my opinion.
But I renamed the section title to: “My browser is eating my apps, and I like it”
a browser like arc
unfortunately Arc’s been axed, would you consider something like zen a worthy alternative?
Unfortunately, not yet…
Arc has a bunch of features that don’t have a direct equivalent in zen (ex: setting custom favicons for tabs, or grouping favorites into folders). It’s also lacking a lot of polish, both in the UI and the available keyboard shortcuts.
But I’m hopeful it will soon be good enough for me to adopt it. In the meantime, I’ll stay on arc.
Aerospace has worked wonders for me.
I heard good things, but is completely useless to me as I only have a single app open most of the time 😅
You can have multiple windows open of a single app?
FWIW, this is how I use the browser – many windows managed by Aerospace.
I know, but I prefer to have a single window, and to manage everything from it.
With arc you can manage multiple workspace from a single window, and split tabs seamlessly. It already answers my (simple) window management needs
I would love it, if window managers had more control over web browser tabs. The window-within-window mechanism should be something that the window manager could control to create a coherent ux along with controlling windows.
In KDE there is a window selector that will let you select specific tabs using fuzzy matching on the title.
There use to be some extensions that would force all tabs to their own windows. Then the OS wm would be in charge.
No, the browser is absolutely awful as a window manager
I feel the same way about tmux and yet I use it on MacOS and Linux without ever detaching from it. Yes, on Linux I could get a better WM, but then my muscle memory will be non-portable across OS
As I read this article - I went from “this is cursed!” to a growing question if others feel this way about Emacs users.
The case against: ^w
I don’t actively use a terminal emulator app on my computer anymore, and use tweety instead from my web browser. I think you should consider doing the same.
I take it that the author is sincere in this, but the motivation seems quite thin.
The browser maybe a better window manager than the default on Windows or macOS, but certainly not on other platforms. (Also, I understand from recent posts that there are a lot of third-party window manager tools for these platforms—e.g., this post on jwno for Windows.)
Bookmarking commands seems reasonably useful, but it’s worth considering why this doesn’t really exist as a feature for traditional terminals. In general, if we want to save commands for later, we want to save them in a fashion that allows for modalities. In the traditional approach, we save them into a source file as a Shell alias or a Shell function.
Bookmarking commands seems reasonably useful, but it’s worth considering why this doesn’t really exist as a feature for traditional terminals.
Having a stable url for each command has some additional benefits though. It allows me to integrate tweety with external tools like raycast quicklinks for example.
This is something that has been in my head for a while, I’m surprised there’s not more on this.
I think an Jupyter HTML-style shell would be interesting. You could even run commands that produce HTML output, or a standardized way for processes to spawn HTTP servers that would be embedded in the terminal as the “result” of the command. (E.g. I run “webtop”, it spawns a web server with a webapp that is like top, but web-based. So it can have SVG graphs, etc. It prints to stdout an HTTP endpoint, the terminal iframes that.)
Or even redirect stdout, stderr, or both to a separate tab.
I think some modern terminals are Electron wrappers, but embracing the brower might bring unexpected features.
…
On the other hand, I kinda also hate web technologies- stuff always tends to be bloated with slow Javascript programs, so I don’t know if I want people to go there.
Yeah I’ve been playing with that idea ! I think using native messaging is probably the best way to handle in a cross-browser way.
On macos, you can use applescript to control a browser from the shell (ex: see https://github.com/pomdtr/arc).
But yeah, I’m deeply convinced that the browser is missing a shell.
How come no one is discussing the security implications of this? You’re turning the ability to access a given port on your computer to the ability to run commands as your user. How does that fit in with the security model of everything else on your computer?
Seems to me like there’s a missing link here, something like a step that can authenticate your browser and leave a cookie etc.
There’s something quite poetic about using a web browser with the most advanced sandboxing capabilities available, hundreds of thousands of human hours invested into creating a complex application platform delicately isolated from the rest of the system, and then going out of your way to create an API which offers arbitrary command execution to literally every website
(That probably says something about the limitations of the www as an application problem - I think OP is trying to do a very reasonable thing, but in the context of the web it’s a very easy problem to shoot yourself in the foot with)
If I ran a web shell on my own machine, it would either be exposed over VPN or behind mTLS. If an adversary can get into my VPN or through my mTLS, they probably weren’t going to struggle to RCE me anyway.
So, any process running on your computer now automatically has all of your user’s privileges. There’s no nobody
daemon anymore, they’re all you
.
Honestly folks, this is not how you approach security. “Security via lack of imagination” is not a valid security posture.
For my personal machines, I am perfectly content with “dies to an adversary that can break TLS” as a security posture.
I think I found a better security model for tweety: distributing it as a chrome extension.
chrome-extension://
schemeHere is the code if you’re interested in taking a look at it: https://github.com/pomdtr/tweety/tree/chrome-extension
Thank you for taking the security feedback in good faith. This definitely sounds much safer as I don’t see any obvious privilege escalation route. I’ll try to take a closer look at the code when I find the time.
This is currently an experiment I’m sharing with the community. There a bunch of way to secure this:
I’m sorry, but I just don’t think this is a proper approach to security in general. You’re opening a huge security hole on your computer and then papering over with “a bunch of ways you could secure this”.
I guess you have a point. I just added support for basic auth: https://github.com/pomdtr/tweety/commit/90bfabfcd67947ed4e0cba69f27e323e4615f56c
I used the Secure Shell extension for Chrome for years (back then it was backed by NaCl and worked incredibly well). It was one of the best ways to get a terminal app on Windows with nice font rendering, storing “favorites” (connections), and overall get nice browser-related behavior (resize by zoom, restored when reopening the app, etc)
Tweety started out as a chrome extension ! It used native messaging instead of a websocket to communicate with the golang binary: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging
I might go back to this design at some point, I think it could allow for some cool integrations.
I don’t understand the entrypoint
script.
But of course, you don’t want to directly exec shell commands coming from random urls, so you’ll need to sanitize the input from your script and only allow a set of commands and arguments that you trust.
But unless you open up your computer to traffic from the internet, the only urls that get hit are ones you type into your browser, right?
As a general rule, you should only allow commands that do not perform any destructive actions, such as rm, mv, or cp.
So what do you do when you need to move a file? Open up a native terminal on your computer? Isn’t the point of this whole post to not need a native terminal?
But also, this script allowlists bash
. So if you run bash
, you can then run rm
or any destructive command you want. So what’s the point in denylisting rm
, if you can still run it after first running bash
?
I feel like I’m missing something here.
You can get hit by an external website redirecting your browser to a URL, rather than typing it yourself.
With the final implementation, bash can be opened in redirect but cannot have commands automatically run. You can type what you want when you open bash.
Basically, you want to protect yourself against nasty redirects.
Ex: https://tweety.localhost/?cmd=rm+-rf+~
. In the script provided in the blog post, you can run bash
, but you will not be able to provide args to it
I couldn’t do this; the last thing I want touching a browser is my terminal, but my hat is off to the author for going all in on it. Feels like one of those “because we could, we never thought about if we should” ideas – yes I know the quote is wrong, I can’t remember it off the top of my head.
Wow, this is certainly commitment! After being a web developer for so long and kinda fed up already with the browser and have been going all the way back to developing desktop applications with Qt. I know there there are a lot of advances in browsers, but if I want to run strace
and have 400 Mb of history, my non-electron terminal can handle that like a breeze…
I admire the authors commitment but it’s kinda crazy when you build a simple unoptimized Qt/C++ desktop app and realize that you can traverse and process millions of data points and render them with no noticeable delay (think the data crunching of DSP and SDR)… I can’t do that in the browser
But of course, you don’t want to directly exec shell commands coming from GET requests, as someone could just easily redirect you to a malicious url like http://localhost:9999/?cmd=rm+-rf+%2F and delete your entire filesystem. So we’ll the input use our entrypoint scripts to only allow a set of commands and arguments that we trust. As a general rule, you should only allow commands that do not perform any destructive actions, such as rm, mv, or cp and instead use interactive commands like nvim, htop or ssh that requires user input to perform actions.
This is terrible advice. What about ssh localhost rm -rf /
, which is not necessarily interactive? What about nvim 'term://rm -rf /'
?
ssh localhost rm -rf /
will not work with the entrypoint script linked in the blog post as the ssh
subcommand only accept a single positional argument.
I was not aware of the nvim term://
trick, I’ll add check of the existence of the file in the entrypoint script.
On this path lies the madness of a constantly growing filter.
“I’ll enumerate all the bad things and stop them” is never good enough for non-trivial systems. The opposite approach, “I’ll enumerate all the proven good things and allow them” works, but is limiting.
tweety supports passing args to a script using by passing the args query parameter.
I don’t think this is sustainable from a security pov. It would be more secure for tweety to have its own bookmarking system, where each bookmark has its own URL like http://localhost/bookmarks/<UUID>
, and parameters for each bookmark are explicitly defined by the user.
Yeah I’m still trying to figure out the best API for this. I think tauri has an interesting model for this: https://tauri.app/reference/javascript/shell/#restricting-access-to-the-command-apis
Browser bookmarks for shell commands? Count me out please. Clearly somebody doesn’t actually spend that much time using CLI tools. Maybe try atuin
or something that actually makes the shell experience better instead of just papering over the bits you can’t be bothered to remember.
no need to be condescending and accuse OP of being “unwilling to learn.” especially since you also don’t seem to be open to new ideas.
I didn’t say they were unwilling to learn, clearly that setup took a lot of experimentation. However it seems to be more learning for novelties sake than productivity. Remembering a few fragments of commands and using that to search for the exact thing from history using atuin will be way faster and more robust than using a mouse to click on a bookmark. And I’m open to new ideas if they have something to offer but the fact that they consider a browsers tab a better may to manage windows than an actual window manager is does not put anything on the table. There are so many actually good window managers out there and browsers are so weak in this area — that isn’t me being not open to new ideas.
Everything except for the Caddy setup is seamless, really love it! My only gripes are that the cursor bar is not customizable, the themes are difficult to customize, and copy/paste is not working great for me.
I’m experimenting with distributing the app as a chrome extension. It removes the need for both caddy and registering tweety as a service.
Thanks for your feedback!
Fortinet’s intrusion prevention guard is claiming your domain name is used for fishing :( I’ve sent in a correction so hopefully they fix it.
On topic, you might want to check that the provided filename for the nvim command actually corresponds to a file; I’m not familiar with Deno’s command runner, but it’s quite common to see shell escapes of the form nvim -c !whoami
If I try to go https:tweety.localhost?cmd=nvim+-c+!whoami
, I’ll get a parsing error since the nvim
is configured to only accept a single positional arg in my code. I don’t see an easy way to exploit that, but I might be wrong !
Thanks for sending the correction!
meanwhile me thinking if i can work just in tty without even launching display server.
But I enjoyed this article, some ideas are quite nice. What I’m afraid that it will become standard and (needed) terminal emulator v2 will be web based.
The tweety repository shows this command: tweety --host 0.0.0.0 --port 8080 <script-path>
. Won’t this let anyone on your network run commands in your shell?
Yeah that was just a way to show the usage of the host flag, but it is obviously a bad idea. I might add support for basic/forward auth, and openid connect in the future which would help secure this