Table of Contents
Pipe #
Pipe is a simple and secure way of putting together composable directional streams of data, just like a *nix |
operator!
For example, maybe you have a *nix pipe you're using on the command line like so:
1tail -f -n 0 /tmp/foo.log | grep --line-buffered "ERROR" | xargs -I{} -L1 osascript -e 'display notification "{}" with title "Pipe Notification"'
This simple one liner will grab new messages from a log file, check if it contains ERROR
and then send a notification for macOS using AppleScript
. This works if you run your app locally, but what if you run the app on a remote server? You can use pipe
to connect remote and local without ever leaving the command line!
On your remote side, you would start a tail and pub
it to a topic:
1tail -f -n 0 /tmp/foo.log | ssh pipe.pico.sh pub foo.log
On your local side, you would sub
it to the notify command:
1ssh pipe.pico.sh sub foo.log | grep --line-buffered "ERROR" | xargs -I{} -L1 osascript -e 'display notification "{}" with title "Pipe Notification"'
The beauty of this method is that the command will wait until the sub
is started before any data is consumed, ensuring you never miss a log line. If you didn't want it to wait for a sub
, you can add -b=false
(b
for blocking) to the pub
to prevent it from blocking.
Once you stop the pub
command, the sub
will also exit. You can also prevent this by adding -k
(k
for keepalive) to the sub
command. With both blocking disabled and keepalive enabled, the commands would look like so:
Remote:
1tail -f -n 0 /tmp/foo.log | ssh pipe.pico.sh pub -b=false foo.log
Local:
1ssh pipe.pico.sh sub -k foo.log | grep --line-buffered "ERROR" | xargs -I{} -L1 osascript -e 'display notification "{}" with title "Pipe Notification"'
We can combine this with any commands we want and create a pretty robust pub/sub system. We can even send full command output through a pipe:
1ssh pipe.pico.sh sub htop
1htop | ssh pipe.pico.sh pub htop
Now what if you wanted to have bi-directional IO? That's where our last command of Pipe comes in, pipe
! Pipe allow you to open a bi-directional client on any topic. It's fully non-blocking and can allow you to do things like have fully interactive chat over pipe. For example, running the following in two terminals:
1ssh pipe.pico.sh pipe chat
Will allow you to type and read messages as if you were sitting at the same terminal!
Pipe Documentation #
General help #
1~$ ssh pipe.pico.sh help
2Command: ssh pipe.pico.sh <help | ls | pub | sub | pipe> <topic> [-h | args...]
3
4The simplest authenticated pubsub system. Send messages through
5user-defined topics. Topics are private to the authenticated
6ssh user. The default pubsub model is multicast with bidirectional
7blocking, meaning a publisher ("pub") will send its message to all
8subscribers ("sub"). Further, both "pub" and "sub" will wait for
9at least one event to be sent or received. Pipe ("pipe") allows
10for bidirectional messages to be sent between any clients connected
11to a pipe.
12
13Think of these different commands in terms of the direction the
14data is being sent:
15
16- pub => writes to client
17- sub => reads from client
18- pipe => read and write between clients
Subs #
1~$ ssh pipe.pico.sh sub -h
2Usage: sub <topic> [args...]
3Args:
4 -a string
5 Comma separated list of pico usernames or ssh-key fingerprints to allow access to a topic
6 -c Don't send status messages
7 -k Keep the subscription alive even after the publisher has died
8 -p Subscribe to a public topic
Pubs #
1~$ ssh pipe.pico.sh pub -h
2Usage: pub <topic> [args...]
3Args:
4 -a string
5 Comma separated list of pico usernames or ssh-key fingerprints to allow access to a topic
6 -b Block writes until a subscriber is available (default true)
7 -c Don't send status messages
8 -e Send an empty message to subs
9 -p Publish message to public topic
10 -t duration
11 Timeout as a Go duration to block for a subscriber to be available. Valid time units are 'ns', 'us' (or 'µs'), 'ms', 's', 'm', 'h'. Default is 30 days. (default 720h0m0s)
Pipes #
1~$ ssh pipe.pico.sh pipe -h
2Usage: pipe <topic> [args...]
3Args:
4 -a string
5 Comma separated list of pico usernames or ssh-key fingerprints to allow access to a topic
6 -c Don't send status messages
7 -p Pipe to a public topic
8 -r Replay messages to the client that sent it
Pipe-web #
Now what if you don't have a terminal available? Not a problem! Pipe has a web component that works side by side. For example, let's start a notification sub
through the terminal like so (not p
for public, so anyone can send us a notification):
1ssh pipe.pico.sh sub -p -k notifications | xargs -I{} -L1 osascript -e 'display notification "{}" with title "Pipe Notification"'
We can send a POST
to the Pipe-web service to send a message onto that topic like so:
1echo "Hello world" | curl -X POST https://pipe.pico.sh/topic/notifications --data-binary @-
And a notification will pop up! Now it's important to note that this is risky, anyone can use a "p
ublic" topic (on both the terminal or Pipe-web). We can make this less risky by starting the notifications topic with an access list set. And if we want Pipe-web to access it, we need to provide "pico" to the access list setting:
1ssh pipe.pico.sh sub -a pico -p -k notifications | xargs -I{} -L1 osascript -e 'display notification "{}" with title "Pipe Notification"'
Now, only Pipe-web and yourself are able to access this public topic.
Pipe-web comes with a few caveats, namely all topics need to be public for it to work. You can set an access list on the topic, but Pipe-web is an unauthenticated service. Therefore, anyone can send a post request to the process.
Pipe-web Documentation #
Subscribe to a topic #
GET
/topic/:topic
subscribe to a topic
Parameters #
name type data type description topic required string topic name to subscribe to
Query Parameters #
name type data type description persist optional boolean Persist the subscription after the publisher closes access optional string Comma separated list of permissible accessors mime optional string Content type to return to the client
Responses #
http code content-type response 200
text/plain;charset=UTF-8
ormime
query parameterSubscription data. Will hang until a pub occurs
Example cURL #
1 curl -vvv https://pipe.pico.sh/topic/test?persist=true
Publish to a topic #
POST
/topic/:topic
publish to a topic
Parameters #
name type data type description topic required string topic name to subscribe to
Query Parameters #
name type data type description access optional string Comma separated list of permissible accessors
Responses #
http code content-type response 200
No content returned
Example cURL #
1 curl -vvvv https://pipe.pico.sh/topic/test -d "hello"
Subscribe to a topic #
GET
/pubsub/:topic
subscribe to a topic
Parameters #
name type data type description topic required string topic name to subscribe to
Query Parameters #
name type data type description persist optional boolean Persist the subscription after the publisher closes access optional string Comma separated list of permissible accessors mime optional string Content type to return to the client
Responses #
http code content-type response 200
text/plain;charset=UTF-8
ormime
query parameterSubscription data. Will hang until a pub occurs
Example cURL #
1 curl -vvv https://pipe.pico.sh/pubsub/test?persist=true
Publish to a topic #
POST
/pubsub/:topic
publish to a topic
Parameters #
name type data type description topic required string topic name to subscribe to
Query Parameters #
name type data type description access optional string Comma separated list of permissible accessors
Responses #
http code content-type response 200
No content returned
Example cURL #
1 curl -vvvv https://pipe.pico.sh/pubsub/test -d "hello"