Terminalwire

Terminalwire

Ship a CLI for your web app. No API required.

Terminalwire is in private beta. If you'd like early access shoot me an email, brad@terminalwire.com, and tell me about your project.

Discuss Your Requirements

A 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 a application_terminal.rb that’s a Thor command-line parser.

# ./app/terminals/application_terminal.rb

# Learn how to use Thor at http://whatisthor.com.
class ApplicationTerminal < Thor
  include Terminalwire::Thor

  def self.basename = "my-app"

  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

    print "Password: "
    password = getpass

    if self.current_user = User.authenticate(email, password)
      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

  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.fetch("user_id"))
  end
end

Routes configuration

The ./config/routes.rb file mounts the ApplicationTerminal 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(ApplicationTerminal),
    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>.

  1. Install the Terminalwire CLI.

    $ gem install terminalwire
    
  2. 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
    
  3. Sign up for a Terminalwire account.

  4. 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.
  5. Install the Terminalwire app.

    $ terminalwire install my-app
    
  6. 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.

Sound good? Let's talk! 🤗

Whether your team is building a new command-line app or simplifying existing infrastructure, I'd love to see how we can work together.

Discuss Your Requirements