A mobile app to run scripts on servers

Over the years, whether I was working with a Raspberry Pi or running a server, there would often be one-off scripts that I would want to run. The usual pattern would be:

  • SSH into the server
  • Run a single command
  • Logout

I always thought it would be nice to have a phone app that could easily run those scripts.

I'd try some mobile SSH client but those felt clunky; a full-blown terminal was overkill. I just wanted to quickly run a single script.

Other times I'd write a quick Flask app to listen on obscure ports and URLs to trigger a script. This also felt like overkill, though, because I really didn't want to have to expose some HTTP server on some port just to run a script on occasion.

So I decided to bite the bullet and write the app that I wanted.


The app is simple. It works as follows:

  • Add a server's SSH credentials
  • You'll see a list of the executable files in ~/.hooks-app/hooks
  • Tap one of them and it will run

I myself have started to use this for a few things, including making sure all the containers are running properly on this server.

How it works

What I learned

This was the first time I used React and it's libraries. I have to say that using it, particularly together with Redux, felt elegant and safe. The fact that a global state defines the UI really makes things simpler.

Working with React Native was also a treat. Hot Loading is a wonderful feature that will update your app when you save a file, while maintaining your app's state. This makes UI work so much easier.

I also got to write some native Android code. I used the JSch Java library to do the SSH communication, so I had to write a bridge between that library and the app's JavaScript. Basically, you write a new Java class that implements method (annotated with @ReactMethod) where certain Java types map to the JS types.

Finally, I learned that ES6 is the bomb. Some of my favorite features include:

  • Shorthand property names
    • return { foo, bar, baz }
  • Object destructuring
    • let { width: screenWidth } = Dimensions.get('window')
  • Arrow functions
    • [1, 2, 3].map(x => x + 5)
  • Object spread operator
    • { ...currentState, ...newState }
  • Async / await
    • let x = await somePromiseReturningMethod()

Get the app

I haven't written an iOS version of the native SSH bridge, so for now, the app only works on Android.

Show Comments