Build command-line interfaces for web apps
Ship terminal applications 10× faster using your favorite web framework
No API required. Streams over WebSockets. Don't worry about distributing binaries. Create a developer account to get started or email
Get StartedA dramatically easier way to build terminal apps 🧘
Terminalwire streams command-line applications over WebSockets from your web server to a thin client installed on peoples’ terminals.
That means developers don’t have to worry about building web APIs and instead can focus on building delightful command-line user experiences.
Achieve 10× more for ⅒ the effort 💪
Solo developers and teams create and maintain command-line apps without the burden of building APIs, managing versioning, packaging binaries, or handling incompatible outdated clients.
New features are deployed instantly to the server, just like a web app. No client-side updates necessary. No breaking changes. Deployments can happen frequently, even on Fridays.
Works with existing web stacks 💃
Plug Terminalwire into your favorite web framework, then connect it to any command-line utility library on the server and you’re ready to go.
Developers are no longer limited to “what runs best on the client”–they are free to use their favorite command-line library in their language of choice.
How it Works
Terminalwire streams terminal I/O between a web server and client over WebSockets. This means you can use your preferred command-line parser, like Thor, Rake, or OptParser, on Rails to deliver a delightful command-line application experience to your users.
Getting Started
Terminalwire is in private beta. This section illustrates how it will integrate with Rails apps when it’s released to the public. Terminalwire will be available for other popular web frameworks outside of Rails and Ruby.
Install Terminalwire
Add the Terminalwire Rails gem to your app.
$ bundle add terminalwire
Then install Terminalwire in your Rails app.
$ rails g terminalwire:install my-app
Configure the terminal
The rails g terminalwire:install my-app
command creates files that you may configure for your application’s needs.
Thor command-line parser
The ./app/terminals
folder has two files.
The application_terminal.rb
file is a Thor command-line parser that sets everything up needed to stream terminal I/O between the client and server, sets the basename
for the command-line application, and provides a current_user
method to authenticate users that other subclasses and commands can use.
# ./app/terminals/application_terminal.rb # Learn how to use Thor at http://whatisthor.com. class ApplicationTerminal < Thor # Enables IO Streaming. include Terminalwire::Thor # The name of your binary. Thor uses this for its help output. def self.basename = "<%= binary_name %>" private def current_user=(user) # The Session object is a hash-like object that encrypts and signs a hash that's # stored on the client's file sytem. Conceptually, it's similar to Rails signed # and encrypted client-side cookies. session["user_id"] = user.id end def current_user @current_user ||= User.find(session["user_id"]) end end
The main_terminal.rb
file is where all your application’s commands are defined. You can add, remove, or modify commands as needed. This file inherits from ApplicationTerminal
and includes the Terminalwire::Thor
module to enable I/O streaming and current_user
authentication needed to manage sessions.
# ./app/terminals/main_terminal.rb class MainTerminal < ApplicationTerminal desc "hello NAME", "say hello to NAME" def hello(name) puts "Hello #{name}" end desc "login", "Login to your account" def login print "Email: " email = gets.chomp print "Password: " password = getpass # Replace this with your own authentication logic; this is an example # of how you might do this with Devise. user = User.find_for_authentication(email: email) if user && user.valid_password?(password) self.current_user = user puts "Successfully logged in as #{current_user.email}." else puts "Could not find a user with that email and password." end end desc "whoami", "Displays current user information." def whoami if self.current_user puts "Logged in as #{current_user.email}." else puts "Not logged in. Run `#{self.class.basename} login` to login." end end desc "logout", "Logout of your account" def logout session.reset puts "Successfully logged out." end end
Routes configuration
The ./config/routes.rb
file mounts the MainTerminal
in a Terminalwire::Thor::Server
WebSocket server to the /terminal
endpoint. This is the URL your client will connect to.
# ./config/routes.rb Rails.application.routes.draw do match '/terminal', to: Terminalwire::Thor::Server.new(MainTerminal), via: [:get, :connect] end
Terminalwire binary stub configuration
Terminalwire generates a binary stub in the Rails ./bin
folder that you may use to interact with your application in a development environment. You may need to change the host if the server is running on a different port.
#!/usr/bin/env terminalwire-exec url: "ws://localhost:3000"
To test the intergration, restart your Rails server, then run ./bin/my-app version
in your terminal and you should see the following:
$ ./bin/my-app version
1.0.0
Going to production
Terminalwire can be distributed through the Terminalwire command-line application via terminalwire install <my-app>
.
Install the Terminalwire CLI.
$ gem install terminalwire
Add the Terminalwire binary path to your
$PATH
environment variable by adding the following to your~/.bashrc
file (or equivalent).# Add to your ~/.bashrc, ~/.zshrc, ~/.shrc, or equivalent file. export PATH=$HOME/.terminalwire/bin:$PATH
Sign up for a Terminalwire account.
Create a new Terminalwire application entry with the following information:
- Name - The name of the application, like “My App”
- URL - The URL where your Terminalwire application can be accessed. Likely at
wss://example.com/terminal
. - Binary Name - The command people will run to execute your application, like
my-app
. - Description - A brief overview of your app that’s displayed in
terminalwire apps
.
Install the Terminalwire app.
$ terminalwire install my-app
Run the app.
$ my-app
That’s it! When users run the terminal app, it streams input and output to the Terminalwire app running on your web server. Updates on the server are automatically reflected on the client side.
Frequently asked questions
How is Terminalwire different from a REST API?
Terminalwire is a WebSocket-based protocol that streams terminal I/O, and other channels, between a web server and client. This allows you to use your preferred command-line parser within your favorite web server framework to deliver a delightful CLI experience to your users.
Can I use Terminalwire with other web frameworks?
Yes! Terminalwire is designed to be integrated with any web framework and server that supports WebSockets.
How is Terminalwire different than SSH?
SSH was created in 1995 to secure, encrypted interactive remote shell sessions. It includes features not needed by modern command-line apps for web applications, such as file transfer, port forwarding, and public-key authentication. Today’s command-line applications for web apps need to do things SSH can’t, like open a web browser on the client to authenticate via the web application. Terminalwire was built specifically to solve problems that modern command-line web apps demand that SSH can’t.
How do I handle authentication and authorization?
Terminalwire integrates with your existing web app’s authentication and authorization mechanisms. You can use the same authentication and authorization methods you use for your web app.
Is Terminalwire encrypted?
Yup. Terminalwire uses the same TLS encryption as your web server and browser, which is WebSockets Secure (WSS) in a production web environment.
How will it be licensed?
Terminalwire will be available under a dual license. One license will be freely available for personal use. Commercial licenses will also be available for organizations.
How can I get early access to Terminalwire?
I’m working with select customers in private beta to ensure that Terminalwire meets their needs and integrating it into my own production web applications. I expect to release Terminalwire to the public in the coming months. If you’d like to integrate Terminalwire into your stack today, email brad@terminalwire.com and let’s talk about your requirements.