Eric Bower
·
22 Oct 24
marketing.page.tmpl
1{{template "base" .}}
2
3{{define "title"}}pipe: authenticated pubsub over ssh{{end}}
4
5{{define "meta"}}
6<meta name="description" content="authenticated *nix pipes over ssh" />
7
8<meta property="og:type" content="website">
9<meta property="og:site_name" content="{{.Site.Domain}}">
10<meta property="og:url" content="https://{{.Site.Domain}}">
11<meta property="og:title" content="{{.Site.Domain}}">
12<meta property="og:description" content="pubsub using ssh">
13{{end}}
14
15{{define "attrs"}}class="container-sm"{{end}}
16
17{{define "body"}}
18<header class="flex flex-col items-center gap-2">
19 <div class="flex flex-col">
20 <img src="https://pico.sh/logo.svg" alt="pico logo" width="50" height="50" />
21 <canvas id="canvas" width="50" height="50"></canvas>
22 <hr width="100%" class="m-0" />
23 </div>
24 <h1 class="text-2xl font-bold text-center">Authenticated *nix pipes over ssh</h1>
25</header>
26
27<article class="flex flex-col gap-2">
28 <div>
29 <p>
30 The simplest authenticated pubsub system. Send messages through
31 user-defined topics (aka channels). By default, topics are private to the authenticated
32 ssh user. The default pubsub model is multicast with bidirectional
33 blocking, meaning a publisher (<code>pub</code>) will send its message to all
34 subscribers (<code>sub</code>) for a topic. There can be many publishers
35 and many subscribers on a topic. Further, both <code>pub</code> and
36 <code>sub</code> will wait for at least one event to be sent or received on the topic.
37 </p>
38
39 <h2 class="text-lg">Features</h2>
40
41 <ol>
42 <li>Familiar *nix pipes API</li>
43 <li>Zero-install</li>
44 <li>Authenticated pubsub using ssh</li>
45 <li>Private pubsub by default</li>
46 <li>Public pubsub by topic (opt-in)</li>
47 <li>Multicast (many pubs to many subs)</li>
48 <li>Bidirectional (e.g. chat)</li>
49 <li>Configurable blocking characteristics for <code>pub</code></li>
50 <li>Configurable timeout for <code>pub</code></li>
51 <li>Paradigms for connecting to a topic:
52 <ol>
53 <li>Read (<code>sub</code>)</li>
54 <li>Write (<code>pub</code>)</li>
55 <li>Read & Write (<code>pipe</code>)</li>
56 </ol>
57 </li>
58 </ol>
59
60 <blockquote>This service is undergoing active development, expect bugs, feature dev, and server restarts</blockquote>
61 </div>
62
63 <div>
64 <h2 class="text-xl">A basic API</h2>
65 <p>Pipe some data into our ssh app and we will send it to anyone listening.</p>
66 <pre>ssh {{.Site.Domain}} sub mykey</pre>
67 <pre>echo "hello world!" | ssh {{.Site.Domain}} pub mykey</pre>
68
69 <details>
70 <summary>Demo</summary>
71 <script
72 src="https://asciinema.org/a/679717.js"
73 id="asciicast-679717"
74 async="true"
75 data-theme="dracula"
76 data-loop="true"
77 data-speed="1.5"
78 data-idle-time-limit="2"
79 ></script>
80 </details>
81 </div>
82
83 <div>
84 <h2 class="text-xl">Simple desktop notifications</h2>
85 <p>Want to quickly receive a notification when a job is done? It can be as simple as:</p>
86 <pre>ssh {{.Site.Domain}} sub notify; notify-send "job done!"</pre>
87 <pre>./longjob.sh; ssh {{.Site.Domain}} pub notify -e</pre>
88 </div>
89
90 <div>
91 <h2 class="text-xl">File sharing</h2>
92 <p>Sometimes you need data exfiltration and all you have is SSH:</p>
93 <pre>cat doc.md | ssh {{.Site.Domain}} pub thedoc</pre>
94 <pre>ssh {{.Site.Domain}} sub thedoc > ./important.md</pre>
95
96 <details>
97 <summary>Demo</summary>
98 <script
99 src="https://asciinema.org/a/679715.js"
100 id="asciicast-679715"
101 async="true"
102 data-theme="dracula"
103 data-loop="true"
104 data-speed="1.5"
105 data-idle-time-limit="2"
106 ></script>
107 </details>
108 </div>
109
110 <div>
111 <h2 class="text-xl">Pipe command output</h2>
112 <p>
113 Send command output through our <code>pipe</code> command. The
114 <code>pipe</code> command is just like <code>pub</code> except it
115 is non-blocking and also acts like a <code>sub</code>. So a client
116 that can read and write to the topic.
117 </p>
118 <pre>ssh {{.Site.Domain}} sub htop</pre>
119 <pre>htop | ssh {{.Site.Domain}} pipe htop</pre>
120
121 <details>
122 <summary>Demo</summary>
123 <script
124 src="https://asciinema.org/a/679712.js"
125 id="asciicast-679712"
126 async="true"
127 data-theme="dracula"
128 data-loop="true"
129 data-speed="1.5"
130 data-idle-time-limit="2"
131 ></script>
132 </details>
133 </div>
134
135 <div>
136 <h2 class="text-xl">Chat</h2>
137 <p>Use our <code>pipe</code> command to have a chat with someone.</p>
138 <pre>ssh {{.Site.Domain}} pipe mychan -p</pre>
139 <p>
140 Now anyone with a <code>pico</code> account can subscribe to this
141 topic using the same command and start typing!
142 </p>
143
144 <details>
145 <summary>Demo</summary>
146 <script
147 src="https://asciinema.org/a/679709.js"
148 id="asciicast-679709"
149 async="true"
150 data-theme="dracula"
151 data-loop="true"
152 data-speed="1.5"
153 data-idle-time-limit="2"
154 ></script>
155 </details>
156 </div>
157
158 <div>
159 <h2 class="text-xl">Pipe reverse shell</h2>
160 <p>If you squint hard enough you can give users interactive access to your shell.</p>
161 <pre>mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | ssh {{.Site.Domain}} pipe myshell > /tmp/f</pre>
162 <pre>ssh {{.Site.Domain}} pipe myshell</pre>
163
164 <details>
165 <summary>Demo</summary>
166 <script
167 src="https://asciinema.org/a/679704.js"
168 id="asciicast-679704"
169 async="true"
170 data-theme="dracula"
171 data-loop="true"
172 data-speed="1.5"
173 data-idle-time-limit="2"
174 ></script>
175 </details>
176 </div>
177
178 <div>
179 <h2 class="text-xl">Simple CI/CD</h2>
180 <p>
181 I'm always looking for easy ways to simplify deploying apps
182 automatically. Having an authenticated, zero-install event system
183 seems handy for this purpose.
184 </p>
185 <pre>while true; do ssh {{.Site.Domain}} sub deploy-app; docker compose pull && docker compose up -d; done</pre>
186 <pre>docker buildx build --push -t myapp .; ssh {{.Site.Domain}} pub deploy-app -e</pre>
187 </div>
188
189 <div>
190 <h2 class="text-xl">Pubsub interactions</h2>
191
192 <h3 class="text-lg">Multiple subs</h3>
193 <p>
194 Have many subscribers, they will all receive the message.
195 </p>
196 <pre>ssh {{.Site.Domain}} sub foobar</pre>
197 <pre>ssh {{.Site.Domain}} sub foobar</pre>
198 <pre>while true; do echo "foobar1"; sleep 1; done | ssh {{.Site.Domain}} pub foobar</pre>
199
200 <details>
201 <summary>Demo</summary>
202 <script
203 src="https://asciinema.org/a/679699.js"
204 id="asciicast-679699"
205 async="true"
206 data-theme="dracula"
207 data-loop="true"
208 data-speed="1.5"
209 data-idle-time-limit="2"
210 ></script>
211 </details>
212
213 <h3 class="text-lg">Multiple pubs</h3>
214 <p>Have many publishers send messages to subscribers.</p>
215 <pre>while true; do echo "foobar1"; sleep 1; done | ssh {{.Site.Domain}} pub foobar</pre>
216 <pre>while true; do echo "foobar2"; sleep 1; done | ssh {{.Site.Domain}} pub foobar</pre>
217 <pre>ssh {{.Site.Domain}} sub foobar</pre>
218
219 <details>
220 <summary>Demo</summary>
221 <script
222 src="https://asciinema.org/a/679698.js"
223 id="asciicast-679698"
224 async="true"
225 data-theme="dracula"
226 data-loop="true"
227 data-speed="1.5"
228 data-idle-time-limit="2"
229 ></script>
230 </details>
231
232 <h3 class="text-lg">Multiple pubs and subs</h3>
233 <p>Have many publishers send messages to many subscribers.</p>
234 <pre>ssh {{.Site.Domain}} sub foobar</pre>
235 <pre>ssh {{.Site.Domain}} sub foobar</pre>
236 <pre>while true; do echo "foobar1"; sleep 1; done | ssh {{.Site.Domain}} pub foobar</pre>
237 <pre>while true; do echo "foobar2"; sleep 1; done | ssh {{.Site.Domain}} pub foobar</pre>
238
239 <details>
240 <summary>Demo</summary>
241 <script
242 src="https://asciinema.org/a/679694.js"
243 id="asciicast-679694"
244 async="true"
245 data-theme="dracula"
246 data-loop="true"
247 data-speed="1.5"
248 data-idle-time-limit="2"
249 ></script>
250 </details>
251 </div>
252
253 <div>
254 <h2 class="text-xl">Send a public message</h2>
255 <pre>echo "hello world!" | ssh {{.Site.Domain}} pub mychan -p</pre>
256 <p>Now anyone with a <code>pico</code> account can subscribe to this topic:</p>
257 <pre>ssh {{.Site.Domain}} sub mychan -p</pre>
258 </div>
259
260 <div>
261 <h2 class="text-xl">Caveats</h2>
262 <p>
263 You must always pipe something into <code>pub</code> or else it will block
264 indefinitely until the process is killed. However, you can provide a
265 flag to send an empty message: <code>pub xyz -e</code>.
266 </p>
267 </div>
268
269 <div>
270 <h2 class="text-xl">Inspiration</h2>
271 <p>
272 A special thanks to <a href="https://patchbay.pub">patchbay.pub</a> for our inspiration.
273 </p>
274 </div>
275
276 <div>
277 <h2 class="text-xl">Latest posts</h2>
278 <div class="flex items-center">
279 <div class="mr font-italic">2024-10-06</div>
280 <div><a href="https://blog.pico.sh/pubsub">pipe: our pubsub ssh service</a></div>
281 </div>
282 </div>
283
284 <div class="text-center mb-2">
285 <p>built on our <a href="https://github.com/picosh/pubsub">go pkg</a>.</p>
286 <a href="https://pico.sh/getting-started" class="btn-link inline-block">GET STARTED</a>
287 </div>
288</article>
289
290{{template "marketing-footer" .}}
291{{end}}