-
Notifications
You must be signed in to change notification settings - Fork 0
/
rss.xml
1211 lines (1119 loc) · 69.7 KB
/
rss.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Everything Burns</title>
<link>https://jimfl.github.io/everything-burns/</link>
<description>Re-ignited</description>
<lastBuildDate>Sat, 18 Nov 23 00:51:39 +0000</lastBuildDate>
<language>en-us</language>
<item>
<title>Early Flickr</title>
<description><![CDATA[
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>Early Flickr - Everything Burns</title>
<link rel="stylesheet" href="/everything-burns/assets/css/style.css">
<link rel="stylesheet" href="/everything-burns/assets/css/prism.css">
<link rel="alternate" type="application/rss+xml" title="Feed for Everything Burns" href="https://jimfl.github.io/everything-burns/rss.xml" />
</head>
<body>
<div id="wrapper">
<header id="top">
<h1><a href="/everything-burns/">Everything Burns</a></h1>
<p>Re-ignited</p>
</header>
<nav>
<ul>
<li><a href="/everything-burns/index.html">Home</a></li>
<li><a href="/everything-burns/posts">Posts</a></li>
<li>Mastodon: <a rel="me" href="https://hachyderm.io/@jimfl">@jimfl@hachyderm.io</a></li>
</ul>
</nav>
<hr>
<main><article class="post"><header><h1 id="early-flickr">Early Flickr</h1><p>
Friday, 17 Nov 2023
<span class="tags">
Tags:
<a class="tag" href="/everything-burns/tags/flickr">flickr</a><a class="tag" href="/everything-burns/tags/history">history</a><a class="tag" href="/everything-burns/tags/web">web</a></span></p></header><div><p><a href="https://www.flickr.com/photos/abulafia/233746/"><img src="/everything-burns/media/flickr/self.jpg" alt="A photo of the author, from Flickr"/></a></p><p>In 1998, I moved back to Seattle after living in Long Island, NY for 4 years. I had managed to wrangle a DSL
internet connection from my previous employer, and had set up a domain, which was running out of my back bedroom.
In late 1999, I discovered weblogs, and eventually created one of my own. In February of 2000, I found out about
a <a href="https://web.archive.org/web/20030201220405/http://www.anitarowland.com/journal/2000/021900.html">local weblog meetup</a>
in the Belltown neighborhood, and showed up. </p><p><img src="/everything-burns/media/flickr/gather.jpg" alt="Grainy photo of the weblog gathering"/></p><p>Through one of the friends I made at that meetup, I got involved with a game
in development by a small group of folks that called themselves Ludicorp. The
game was Game Neverending, or GNE. GNE was a social crafting game. You gather
bits from the map, you combine the bits into more sophisticated bits, and so
on. Because the game was in development, play modes were limited. I remember
the social aspect, and the crafting most. Because of the limits, the players
would get together and create their own objectives. One night I logged in to
find a bunch of players were amusing themselves by trying to gather a large
number of heavy crafted objects (harpsichords) into a node on the map called
Bentown. I thought that was funny, so I took a screenshot, and warped it, as
though the mass of the harspichords were warping gamespace. Emergent gameplay,
unanticipated in its specifics by the development team, was a huge part of
what made GNE attractive.</p><p><img src="/everything-burns/media/flickr/warp.jpg" alt="Warped GNE space"/></p><p>I was able to create this image and share it with the group of people
participating because the GNE in-game chat, which was feature rich to support
the social aspect, supported posting pictures in chat. The chat aspect of the
game received a lot of feature work in the time I was playing.</p><p>One morning I logged into GNE, and Stewart Butterfield (a.k.a. St. Wart) sent me a DM. </p><p>“Have you tried Flickr yet?”</p><p>The Ludicorp crew had seen the potential in a collaborative environment where
people could share images and they pivoted the social chat aspect into a
completely different product. If I remember correctly, the earliest version of
Flickr was still synchronous; Much more like chat than the web experience it
quicky morphed into.</p><p>Many of the earliest Flickr users were also GNE players. And so early on, there
was a lot of emergent play taking place on Flickr as well. One collaborative
effort I recall was The Flickr Tarot, where we would make cards using Flickr,
GNE, and Ludicorp themes. Many GNE users kept their aliases on Flickr. I was
Abulafia (or Abu) on GNE, and so I was Abu on Flickr. One GNE/Flickr user
created a calendar with all our birthdays on it. I imported it at some point
into my Google calendar, and I still get notifications to this day about them.</p><p><img src="/everything-burns/media/flickr/vacapinta.jpg" alt="The Vacapinta"/><img src="/everything-burns/media/flickr/yahoo.jpg" alt="Yahoo!"/></p><p>Flickr also became a common ground for people to gather in the real world and
either embark on photo strolls together, or just meet in a local venue and
chat. I eventually extended my personal Flickr community beyond the original
GNE players, to people in and around the city where I lived.</p><p><img src="/everything-burns/media/flickr/meetup.jpg" alt="Flickr meetup"/></p><p>Digital cameras were just starting to explode. Digital photographers of all
intensity levels were to be found at meetups, eager to talk about their kit.
For my part, I was always on the lookout for a camera that I could have on my
all the time, but not something that was swinging around my neck or a target
for theft; something we can take for granted today. I would occasionally
set out on solo photo safaris around the city, hoping to capture the visual
character and document it on Flickr, but I also wanted to be able to have a
camera on me for happy accidents. I eventually found a credit card sized camera
that took awful photos, then decided to stick to my DSLR.</p><p><img src="/everything-burns/media/flickr/revenge.jpg" alt="Seattle graffiti"/></p><p>Eventually, I came to use Flickr primarily to share travel and outdoor
adventures as well as urban explorations, slowly leaving behind the ludic
early Flickr experiences.</p><p><img src="/everything-burns/media/flickr/snow.jpg" alt="Snowshoeing in the Cascades"/></p><p>Flickr was one of the earliest online services that I can remember to expose
a programmatic API to users. My personal weblog was implemented in self-hosted
MovableType, which had a plugin system allowing users to write extensions in
Perl. I took advantage of that to build a MovableType plugin which called the
Flickr APIs to get a list of albums from my account, and display them in the
sidebar of my blog. As I added new albums on Flickr, they automatically showed
up on my blog. I posted this to the MovableType plugin directory, and forgot
about it until I got an email from Stewart Butterfield letting me know that
people using the plugin were reporting it was broken. There was a change to the
API that I hadn’t known about. My own setup appeared to be working because I
had cached the information locally.</p><p>When Flickr first put out a Pro option, I was an early paying member. I think
the email receipt I got was 0000005. After a couple of acquisitions, I stopped
paying, and the various proprieters keep threatening to delete some of my
photos, but never seem to get around to it. I still use my Flickr account to
help remember things, or place them in context, even though I don’t even think
I can log into it anymore. I used it to jog my memory about this post.</p><p>All of the images in this post, except the second one of the weblog meetup in February of 2000 (which was
taken with an Apple QuickTake 100) were selected from my Flickr account. </p><p>You can see more of my photos, and other random images gleaned from the early web, on <a href="https://www.flickr.com/photos/abulafia/">Flickr</a>.</p></div></article></main>
<hr>
<footer>
<!--=====================================-->
<!-- Override "includes/footer.html.eex" -->
<!-- to customize your footer contents. -->
<!--=====================================-->
<p>
Powered by <a href="https://github.com/Dalgona/Serum">Serum</a> v1.5.1,
with <a href="https://github.com/Dalgona/serum-theme-essence">Essence</a> theme
</p>
</footer>
</div>
<script src="/everything-burns/assets/js/prism.js"></script>
</body>
</html>
<script>
const ws_url = "ws://" + location.host + "/serum_live_reloader";
var ws;
connect();
function connect() {
ws = new WebSocket(ws_url);
ws.onmessage = onMessage;
ws.onclose = onClose;
}
function onMessage(e) {
if (e.data === "reload") {
location.reload();
}
}
function onClose(e) {
console.warn("WebSocket disconnected from server. Reconnecting in 10 seconds.");
setTimeout(connect, 10000)
}
</script>
]]></description>
<pubDate>Sat, 18 Nov 23 00:51:39 +0000</pubDate>
<link>https://jimfl.github.io/everything-burns/posts/2023-11-17-early_flickr.html</link>
<guid>https://jimfl.github.io/everything-burns/posts/2023-11-17-early_flickr.html</guid>
</item>
<item>
<title>Multi-device RSS reading</title>
<description><![CDATA[
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>Multi-device RSS reading - Everything Burns</title>
<link rel="stylesheet" href="/everything-burns/assets/css/style.css">
<link rel="stylesheet" href="/everything-burns/assets/css/prism.css">
<link rel="alternate" type="application/rss+xml" title="Feed for Everything Burns" href="https://jimfl.github.io/everything-burns/rss.xml" />
</head>
<body>
<div id="wrapper">
<header id="top">
<h1><a href="/everything-burns/">Everything Burns</a></h1>
<p>Re-ignited</p>
</header>
<nav>
<ul>
<li><a href="/everything-burns/index.html">Home</a></li>
<li><a href="/everything-burns/posts">Posts</a></li>
<li>Mastodon: <a rel="me" href="https://hachyderm.io/@jimfl">@jimfl@hachyderm.io</a></li>
</ul>
</nav>
<hr>
<main><article class="post"><header><h1 id="multi-device-rss-reading">Multi-device RSS reading</h1><p>
Sunday, 16 Jul 2023
<span class="tags">
Tags:
<a class="tag" href="/everything-burns/tags/rss technology docker">rss technology docker</a></span></p></header><div><p>I see a lot of questions about how one can keep RSS readers synced across
devices in a self-hosted way. Here is a description and a step by step process
of how I have set this up for myself.</p><p>Rather than keeping devices and/or desktops in sync, the approach I am using
is to set up a central source of truth, then point readers across multiple
devices at that server. The server I chose was <a href="https://miniflux.app">Miniflux</a>
than I am running in Docker on a spare laptop.</p><p>Miniflux has its own minimal web interface for managing feeds and catagories,
for searching, as well as for reading. There is also support for filtering,
rewriting, and scraping, which I have not used at all yet, but I like the
potential.</p><p>Miniflux also supports acces via multiple APIs, most notably the Reader API,
but also includes its own (which includes endpoints for creating miniflux
users, and searching). API authentication can be with username/password, or
with API tokens, both of which are set up on a per-user basis.</p><p>Once Miniflux is set up (which we’ll get into below), you need to log into
miniflux with a browser (<a href="http://localhost:80">http://localhost:80</a> by default) as the admin user you
set up in the steps that follow, and create an actual user for reading feeds.
As admin, navigate to the <strong>Users</strong> tab, then fill out the form. You can
decide whether that user also has admin privileges.</p><p>Now log out, then log back in as the user you just created. You can add some
feeds before the next step. To enable apps to talk to Miniflux, Navigate
to <strong>Settings</strong>><strong>Integrations</strong>. I am using an iPad with Reeder 5 which
supports the Reader API, so I would go to the Google Reader section, tick the
<strong>Activate Google Reader API</strong>, and supply a username and password that you’ll
use to log in from the app.</p><p>Miniflux does the work of periodically polling and fetching the RSS feeds, as
well as performing any filtering or scraping you have configured, and building
a full text search. Each feed in miniflux can be configured to fetch the
original article as well. You can use the Miniflux UI (or API) to manage feeds,
or, you can use your APP to di it. The result is the same. Marking articles as
read or starring them is stored in Miniflux, so if you connect with a different
app, all those things are the same across
apps.</p></div></article></main>
<hr>
<footer>
<!--=====================================-->
<!-- Override "includes/footer.html.eex" -->
<!-- to customize your footer contents. -->
<!--=====================================-->
<p>
Powered by <a href="https://github.com/Dalgona/Serum">Serum</a> v1.5.1,
with <a href="https://github.com/Dalgona/serum-theme-essence">Essence</a> theme
</p>
</footer>
</div>
<script src="/everything-burns/assets/js/prism.js"></script>
</body>
</html>
<script>
const ws_url = "ws://" + location.host + "/serum_live_reloader";
var ws;
connect();
function connect() {
ws = new WebSocket(ws_url);
ws.onmessage = onMessage;
ws.onclose = onClose;
}
function onMessage(e) {
if (e.data === "reload") {
location.reload();
}
}
function onClose(e) {
console.warn("WebSocket disconnected from server. Reconnecting in 10 seconds.");
setTimeout(connect, 10000)
}
</script>
]]></description>
<pubDate>Sat, 18 Nov 23 00:51:39 +0000</pubDate>
<link>https://jimfl.github.io/everything-burns/posts/2023-07-16-multi-device-rss.html</link>
<guid>https://jimfl.github.io/everything-burns/posts/2023-07-16-multi-device-rss.html</guid>
</item>
<item>
<title>Disinterest</title>
<description><![CDATA[
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>Disinterest - Everything Burns</title>
<link rel="stylesheet" href="/everything-burns/assets/css/style.css">
<link rel="stylesheet" href="/everything-burns/assets/css/prism.css">
<link rel="alternate" type="application/rss+xml" title="Feed for Everything Burns" href="https://jimfl.github.io/everything-burns/rss.xml" />
</head>
<body>
<div id="wrapper">
<header id="top">
<h1><a href="/everything-burns/">Everything Burns</a></h1>
<p>Re-ignited</p>
</header>
<nav>
<ul>
<li><a href="/everything-burns/index.html">Home</a></li>
<li><a href="/everything-burns/posts">Posts</a></li>
<li>Mastodon: <a rel="me" href="https://hachyderm.io/@jimfl">@jimfl@hachyderm.io</a></li>
</ul>
</nav>
<hr>
<main><article class="post"><header><h1 id="disinterest">Disinterest</h1><p>
Tuesday, 4 Jul 2023
<span class="tags">
Tags:
<a class="tag" href="/everything-burns/tags/advice-and-admonitions-for-young-people">advice-and-admonitions-for-young-people</a></span></p></header><div><p>I used to get annoyed and dejected when it seemed like the senior leadership
in my company, department, or organization seemed to take little interest
in the projects that my team and I were working on. This can all be very
disheartening, and impact the morale of the team putting all their working
energy into the project. Too late in my career I learned to think
about interest by senior leadership in a completely different way. I eventually
learned to take this as a good sign under the right conditions.</p><p>The evidence of disinterest by senior leadership can take the form of</p><ul><li>difficulty getting into meetings with the senior leader
</li><li>inattentiveness/lack of participation in meetings by the senior leader. Open laptop/replying to texts/playing video games (this has happened to me. You know who you are.)
</li><li>Project status/presentations near the end of agendas, making them more likely to be dropped as ballast for time reasons
</li><li>Explicit: “I don’t care about this.” (this has happened to me)
</li></ul><p>For the purposes of what follows, a senior leader is someone either at director
or vice president level. Like you, as an individual contributor, these people
have a bunch of things going on, and like you, they need to carefully choose
their battles, because one person can only handle so much cognitive load before
they start to become very ineffective, whether it’s at a tactical level, or a
strategic one.</p><p>So, if a senior leader takes a concentrated interest in your team’s project,
likely one of the following (sometimes both) is true:</p><ul><li>your project has the potential to make them look very good,
</li><li>your project has the potential to make them look very bad.
</li></ul><p>How can you tell one from the other? If you don’t know which it is, working
with your team’s manager on your own situational awareness is probably a good
topic for an upcoming conversation. Signs that your project has the potential
to make the senior leader look good include:</p><ul><li>delivery dates that coincide with internal or customer facing events where product announcements are made
</li><li>the senior leader is exploring if scope can be expanded
</li><li>the senior leader is asking questions about the customer-facing aspects of the project
</li></ul><p>Signs that it’s the other way around</p><ul><li>the project schedule is in open negotiation
</li><li>the senior leader is exploring if scope can be <em>reduced</em></li><li>the senior leader is asking questions about internal process
</li></ul><p>Either way, your team is likely to be facing some increased pressure to perform, which isn’t always fun.
That being said, it’s possible to learn a great deal from senior leaders when they are working closely
with you and your team. And not always the things they’d prefer you to learn!</p><p>But what can you infer from the apparent lack of interest? Most likely, the
senior leader has delegated the responsibility to the team’s manager or maybe
one level above them. They are trusting that line manager to make reasonable
progress, and the high level signals they receive (status reports, goal-meeting, lack of requests for interference, lack of escalations from dependent
projects) indicate that the project is on track and being well run. It’s not always
like this; sometimes middle management is, in fact, acutely incompetent. But if you’re
giving the benefit of the doubt, the above should hold.</p></div></article></main>
<hr>
<footer>
<!--=====================================-->
<!-- Override "includes/footer.html.eex" -->
<!-- to customize your footer contents. -->
<!--=====================================-->
<p>
Powered by <a href="https://github.com/Dalgona/Serum">Serum</a> v1.5.1,
with <a href="https://github.com/Dalgona/serum-theme-essence">Essence</a> theme
</p>
</footer>
</div>
<script src="/everything-burns/assets/js/prism.js"></script>
</body>
</html>
<script>
const ws_url = "ws://" + location.host + "/serum_live_reloader";
var ws;
connect();
function connect() {
ws = new WebSocket(ws_url);
ws.onmessage = onMessage;
ws.onclose = onClose;
}
function onMessage(e) {
if (e.data === "reload") {
location.reload();
}
}
function onClose(e) {
console.warn("WebSocket disconnected from server. Reconnecting in 10 seconds.");
setTimeout(connect, 10000)
}
</script>
]]></description>
<pubDate>Sat, 18 Nov 23 00:51:39 +0000</pubDate>
<link>https://jimfl.github.io/everything-burns/posts/2023-07-04-disinterest.html</link>
<guid>https://jimfl.github.io/everything-burns/posts/2023-07-04-disinterest.html</guid>
</item>
<item>
<title>Surprising Richness from Simplicity</title>
<description><![CDATA[
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>Surprising Richness from Simplicity - Everything Burns</title>
<link rel="stylesheet" href="/everything-burns/assets/css/style.css">
<link rel="stylesheet" href="/everything-burns/assets/css/prism.css">
<link rel="alternate" type="application/rss+xml" title="Feed for Everything Burns" href="https://jimfl.github.io/everything-burns/rss.xml" />
</head>
<body>
<div id="wrapper">
<header id="top">
<h1><a href="/everything-burns/">Everything Burns</a></h1>
<p>Re-ignited</p>
</header>
<nav>
<ul>
<li><a href="/everything-burns/index.html">Home</a></li>
<li><a href="/everything-burns/posts">Posts</a></li>
<li>Mastodon: <a rel="me" href="https://hachyderm.io/@jimfl">@jimfl@hachyderm.io</a></li>
</ul>
</nav>
<hr>
<main><article class="post"><header><h1 id="surprising-richness-from-simplicity">Surprising Richness from Simplicity</h1><p>
Wednesday, 12 Apr 2023
<span class="tags">
Tags:
<a class="tag" href="/everything-burns/tags/markdown">markdown</a><a class="tag" href="/everything-burns/tags/rss">rss</a></span></p></header><div><p>Recently I have been tinkering with RSS feeds in code, initially working on code to fetch and store the feeds, so
that later I could play around with ways to present and organize feeds and entries. The shape, format, and contents
of RSS feeds vary wildly, and I wanted a way to quickly review what sorts of things I was getting in different
feeds. Markdown seemed like the simplest thing that could possibly work, so I whipped up some code to spit out
a feed as markdown, where each of the entries looks like this</p><pre><code class="">### [When I'm An Astronaut](https://www.metafilter.com/198913/When-Im-An-Astronaut)
* author: box
* date: 2023-04-10
* #dreampop, #ethereal, #indierock, #music, #shoegaze</code></pre><p>into a file named <code class="inline"><feed title>.md</code>. Nothing could be more low-tech.</p><p>I use <a href="https://logseq.com/">Logseq</a> to take notes, which uses markdown to store notes, and can also open
directories of existing markdown files, so I opened the folder in Logseq, and what I saw surprised me. Logseq
creates an index of pages, and creates references between them, wiki-style, if you mention another page by
surrounding its title with <code class="inline">[[]]</code>. Logseq also uses a hashtag notation for grouping pages together by tags. So my format
contained a happy accident. In addition, Logseq has a journal format. Any time you open the journal, it adds a section
for today’s date at the top. If you ping a date in any other page like <code class="inline">[[2023-04-10]]</code>
then the page gets linked into that journal entry for that day. </p><p>Realizing this, I made a simple change to my markdown template to look like this instead:</p><pre><code class="">### [When I'm An Astronaut](https://www.metafilter.com/198913/When-Im-An-Astronaut)
* author: box [[2023-04-10]]
* #[[dreampop]], #[[ethereal]], #[[indierock]], #[[music]], #[[shoegaze]]</code></pre><p>Now, here is what the MetaFilter page looks like in Logseq</p><p><img src="/everything-burns/media/logseq/logseq_feed.png" alt="MetaFilter feed page in Logseq"/></p><p>For the feed pages, I did end up using some Logseq-specific markdown at the top for the feed metadata. Here’s the
<a href="https://hexdocs.pm/eex/main/EEx.html">EEx</a> template:</p><p><img src="/everything-burns/media/logseq/feed_template.png" alt="EEx template"/></p><p>Here’s a page that gets generated for the <code class="inline">#Art</code> tag. Note that this page doesn’t get clobbered when I overwite
the individual feed pages, the entries that are taged <code class="inline">#Art</code> just get linked into the “Linked References” section.
Any notes I type in the page istelf are retained.</p><p><img src="/everything-burns/media/logseq/logseq_tag.png" alt="Art tag page in Logseq"/></p><p>Here’s the journal page. Just like the tag pages, the feed entries are just referenced (and nicely grouped by feed).</p><p><img src="/everything-burns/media/logseq/logseq_journal.png" alt="Journal section in Logseq"/></p><p>All of this is done for free by Logseq. All I did was dump the markdown into one <code class="inline">.md</code> file per feed.</p><p>Here’s perhaps the coolest feature: The Logseq graph view:</p><p><img src="/everything-burns/media/logseq/logseq_graph.png" alt="Graph view in Logseq"/></p><p>This really does work as a very rudimentary feed reader, all kind of by accident.</p><p>One of this things I learned in this exercise is that the RSS feed for this site could use some improvements. I’ll
spare you the screenshot of my Logseq TODO list.</p></div></article></main>
<hr>
<footer>
<!--=====================================-->
<!-- Override "includes/footer.html.eex" -->
<!-- to customize your footer contents. -->
<!--=====================================-->
<p>
Powered by <a href="https://github.com/Dalgona/Serum">Serum</a> v1.5.1,
with <a href="https://github.com/Dalgona/serum-theme-essence">Essence</a> theme
</p>
</footer>
</div>
<script src="/everything-burns/assets/js/prism.js"></script>
</body>
</html>
<script>
const ws_url = "ws://" + location.host + "/serum_live_reloader";
var ws;
connect();
function connect() {
ws = new WebSocket(ws_url);
ws.onmessage = onMessage;
ws.onclose = onClose;
}
function onMessage(e) {
if (e.data === "reload") {
location.reload();
}
}
function onClose(e) {
console.warn("WebSocket disconnected from server. Reconnecting in 10 seconds.");
setTimeout(connect, 10000)
}
</script>
]]></description>
<pubDate>Sat, 18 Nov 23 00:51:39 +0000</pubDate>
<link>https://jimfl.github.io/everything-burns/posts/2023-04-12-simplicity-and-richness.html</link>
<guid>https://jimfl.github.io/everything-burns/posts/2023-04-12-simplicity-and-richness.html</guid>
</item>
<item>
<title>TOFU? STFU!</title>
<description><![CDATA[
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>TOFU? STFU! - Everything Burns</title>
<link rel="stylesheet" href="/everything-burns/assets/css/style.css">
<link rel="stylesheet" href="/everything-burns/assets/css/prism.css">
<link rel="alternate" type="application/rss+xml" title="Feed for Everything Burns" href="https://jimfl.github.io/everything-burns/rss.xml" />
</head>
<body>
<div id="wrapper">
<header id="top">
<h1><a href="/everything-burns/">Everything Burns</a></h1>
<p>Re-ignited</p>
</header>
<nav>
<ul>
<li><a href="/everything-burns/index.html">Home</a></li>
<li><a href="/everything-burns/posts">Posts</a></li>
<li>Mastodon: <a rel="me" href="https://hachyderm.io/@jimfl">@jimfl@hachyderm.io</a></li>
</ul>
</nav>
<hr>
<main><article class="post"><header><h1 id="tofu?-stfu!">TOFU? STFU!</h1><p>
Friday, 24 Mar 2023
</p></header><div><p>This morning was one of those popcorn-for-breakfast days when the tech world
learns about some impactful security event with a package or resource that is
depended upon by many teams across many companies and open source or personal
projects.</p><p>GitHub is more or less a core part of the development tooling for many projects
around the world. This morning, GitHub announced that the RSA SSH private key
material for github.com was exposed, and therefore must be replaced with a
different key. I learned about it by reading about it. Others learned about it
when their automated processes involving GitHub started failing, or when their
manual processes started throwing up scary messages.</p><p>If this event did not break your GitHub connected workflows that use SSH, you
may have a problem. I will try to get into the nature of that problem below.</p><p>In what follows, I will attempt to avoid criticizing GitHub’s handling of this
event or editorialzing about the incident itself. You can help keep me honest
here by reaching out to @jimfl@hachyderm.io on Mastodon. (Other techies commenting
on the incident are fair game though!)</p><p>The key in question is used to authenticate and identify GitHub in interactions
which use SSH as a transport, so that clients have some assurance that they are
indeed talking to GitHub, and not someone trying to impersonate GitHub.</p><h2 id="what-is-at-stake">What is at stake</h2><p>Many GitHub users were inconvenienced by the failure of their automated
processes, the questions about the GitHub authentication failure messages
to the helpdesk, and the need to update all the systems that interact with
GitHub to be able to accept (I am actively trying to avoid the word “trust”
throughout) the replacement key.</p><p>These failures are a good thing, and part of the design of SSH, and again,
the lack of failures needs to be investigated by anyone who “got lucky.”
You should at least verify that you’re using ECDSA or Ed2559 and not RSA keys,
in the absence of an error.
These failures aren’t why many people are upset. And many of the people who
are upset for the right reasons are, in my opinion, still upset for the wrong
reasons.</p><p>It is important to understand what can go wrong if someone is able to trick
a software developer or automated process into beliving that they are GitHub.
This would allow an attacker to inject whatever source code they pleased into
the code of a package, when a package is pulled down from GitHub to be built.
That’s the biggest danger, though there are likely some other tricky things
that can be accomplished by someone in this position.</p><p>One of the nasty things about public-key cryptography is that that private part
of the key must be kept safe and only usable by the party that it means to
authenticate. The private key is just a blob of data from which infinite copies can
be made, and it is almost impossible to detect if a copy has been made.</p><h2 id="tr*st-on-first-use-(tofu)">Tr*st On First Use (TOFU)</h2><p>SSH is used to create encrypted and authenticated connections between computers.
It was introduced in 1995, and has evolved somewhat since then. SSH connections
can be used to protect interactive terminal sessions, run commands on other
computers, or transfer data. Both the client and server has a public-key pair.
The client’s private key is used to authenticate them to the system, and the
server’s private key is used to authenticate the server to the client.</p><p>In order to accept each others keys, the server needs to know the public key
of the client, and the client needs to know the public key of the server.
Many of the use cases for SSH were that a handfull of users (clients) would
be using SSH to connect to a larger number of servers, and so the dynamics of
the way key material was managed reflect that. public key material resides in
files on the filesystem. As a user, you <em>could</em> go around and collect all the
public keys from all the machines you need to talk to, and put them in your
<code class="inline">known_hosts</code> file, but first, that’s a hassle, and second, the server
authenticating itself to the client is <strong>far and away</strong> less important than the
client authenticating to the server (Right? That’s right, isn’t it?)</p><p>So, as a convenience measure, if the client doesn’t have the public key of the
server, it will give the user an opportunity to accept the public key that was
used in the initial connection as the key for that server going forward. This
interaction is called “Trust On First Use,” or TOFU for short. The next time
the client makes a connection to that machine, the public key better be the same,
or an intentionally scary, <code class="inline">ALL CAPS BECAUSE YO, PAY ATTENTION</code> message is
presented to the client.</p><p>SSH TOFU doesn’t work in the other direction. The server needs to be configured with
the client’s key in advance. Commonly, the user logs in with a username/password,
sets up the key, and then can use SSH from that point forward. Sometimes this
will be done by administrators for the clients.</p><h2 id="other-uses-for-ssh">Other uses for SSH</h2><p>SSH was initially designed for these many-to-many, ad-hoc interactions between
machines, where the authentication of the client was placed above the authentication
of the server to the client. </p><p>SSH is a convenient way to arrange protected communications of many kinds, and even
create ad-hoc private networks, tunnels, and proxies between systems. </p><p>Before GitHub, the git source code manager system was able to be configured to use
SSH to authenticate users and encrypt communications between git repositories on
multiple machines. The git system itself is designed to help developers distributed
across multiple computers coordinate changes to software source code. One common
pattern that emerged was that there would be a central “repo” which all the developers
would coordinate with. GitHub is this central repository pattern writ large. SSH is only
one of the ways users authenticate with GitHub, and again, the <em>primary</em> important
authentication is authenticating the client to the server (Right?)</p><h2 id="critical-response">Critical Response</h2><p>Much of the critical response around this incident centers around how GitHub might
have better protected the key material used for their server key, which much
gobsmacked handwringing about why they were not using a Hardware Security Module
to manage the key (HSMs are hard. Really hard. Hard. Escpecially hard to automate
against in a secure manner).</p><p>But really, I see the problem is the attitude, handed down from decades of SSH
use, that the authentication of server to client is secondary to the authentication
of client to server, which leads to acceptance of TOFU, around which there are no
tried and true controls. You can replace the server key, which causes everyone a
bad day (imagine Gary Oldman screaming EV-REE-ONE here), but this interacts with
another phenomenon in a potentially destructive way. That phenomenon is normalization
of deviance. At the end of the day, most of the people who encounter the SSH error
they get from github, are not scared. They’re trying to get something done. They’ve
seen the SSH error a bunch of times. Things go wonky, need to get fixed. Nobody is
actually trying to perpetrate a man-in-the-middle attack. So they will accept the
new key and move on, without deep inspection, and certainly no verification of the
gibberish-looking key signature for the new key.</p><p>What this means is that there is still an attack vector against people who have
not yet tried to connect to GitHub. They’ll get the error, their colleague
will say “Oh, yeah, GitHub had to rotate their key,” and they’ll accept the new
key, which might not be from GitHub.</p><p>The simple fact is that for a critical resource of the scale and impact of
GitHub, authentication of the service is equally important as authentication
of the clients, the TOFU dynamic cannot provide the assurances it should.
You can be assured that some actors are feverishly trying to work out how to
exploit straggler users who have not yet updated their local copy to the new
GitHub key.</p></div></article></main>
<hr>
<footer>
<!--=====================================-->
<!-- Override "includes/footer.html.eex" -->
<!-- to customize your footer contents. -->
<!--=====================================-->
<p>
Powered by <a href="https://github.com/Dalgona/Serum">Serum</a> v1.5.1,
with <a href="https://github.com/Dalgona/serum-theme-essence">Essence</a> theme
</p>
</footer>
</div>
<script src="/everything-burns/assets/js/prism.js"></script>
</body>
</html>
<script>
const ws_url = "ws://" + location.host + "/serum_live_reloader";
var ws;
connect();
function connect() {
ws = new WebSocket(ws_url);
ws.onmessage = onMessage;
ws.onclose = onClose;
}
function onMessage(e) {
if (e.data === "reload") {
location.reload();
}
}
function onClose(e) {
console.warn("WebSocket disconnected from server. Reconnecting in 10 seconds.");
setTimeout(connect, 10000)
}
</script>
]]></description>
<pubDate>Sat, 18 Nov 23 00:51:39 +0000</pubDate>
<link>https://jimfl.github.io/everything-burns/posts/2023-03-24-tofu-stfu.html</link>
<guid>https://jimfl.github.io/everything-burns/posts/2023-03-24-tofu-stfu.html</guid>
</item>
<item>
<title>Cable Management Hack</title>
<description><![CDATA[
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>Cable Management Hack - Everything Burns</title>
<link rel="stylesheet" href="/everything-burns/assets/css/style.css">
<link rel="stylesheet" href="/everything-burns/assets/css/prism.css">
<link rel="alternate" type="application/rss+xml" title="Feed for Everything Burns" href="https://jimfl.github.io/everything-burns/rss.xml" />
</head>
<body>
<div id="wrapper">
<header id="top">
<h1><a href="/everything-burns/">Everything Burns</a></h1>
<p>Re-ignited</p>
</header>
<nav>
<ul>
<li><a href="/everything-burns/index.html">Home</a></li>
<li><a href="/everything-burns/posts">Posts</a></li>
<li>Mastodon: <a rel="me" href="https://hachyderm.io/@jimfl">@jimfl@hachyderm.io</a></li>
</ul>
</nav>
<hr>
<main><article class="post"><header><h1 id="cable-management-hack">Cable Management Hack</h1><p>
Thursday, 29 Dec 2022
<span class="tags">
Tags:
<a class="tag" href="/everything-burns/tags/diy">diy</a><a class="tag" href="/everything-burns/tags/hack">hack</a></span></p></header><div><p>I have a desk that has a very nice, metal cable management grille attached to the rear legs, which is one
of the reasons I bought the desk. </p><p><img src="/everything-burns/media/noodlemgmt/grille.jpg" alt="Cable Management Grille"/></p><p>What I found though is that over time, it just became a very visible
jumble of wires and zip ties. Making any changes was a pain. I have been
itching to tear it out and re-do the cabling to make it pretty again, but
I knew that eventually it would just get jumbled again. I started to noodle
about ways to keep cables secure, but in a flexible, easy to modify way.</p><p>After some noodling, I finally arrived at pool noodles, which we just happen
to have lying around. Here’s what I came up with. Slice the noodle along the
length. The excess cables go into the slot and hang out in the cavity.</p><p>I wanted to run cables through the entire length of the tube if need be, so I
couldn’t just zip tie the whole noodle around the outer circumference to the
grid. So I drilled some holes in the back side of the pool noodle, and run the
zip tie through that, along the length of the tube.</p><p><img src="/everything-burns/media/noodlemgmt/noodle_hack.png" alt="split noodle"/></p><p>I put three sets of zip tie holes along the 32in. length of pool noodle, and
connected it to the back side of the grille, facing the wall. It isn’t really
visible from the front side (where I’ve attached a power strip and a power
brick).</p><p>This works great. I discovered that I don’t even have to stuff the whole cable
into the cavity like I originally envisioned, I can just take the slack into a
loop, and jam the loop into the slot. This makes it easy to re-arrange things as
needed.</p></div></article></main>
<hr>
<footer>
<!--=====================================-->
<!-- Override "includes/footer.html.eex" -->
<!-- to customize your footer contents. -->
<!--=====================================-->
<p>
Powered by <a href="https://github.com/Dalgona/Serum">Serum</a> v1.5.1,
with <a href="https://github.com/Dalgona/serum-theme-essence">Essence</a> theme
</p>
</footer>
</div>
<script src="/everything-burns/assets/js/prism.js"></script>
</body>
</html>
<script>
const ws_url = "ws://" + location.host + "/serum_live_reloader";
var ws;
connect();
function connect() {
ws = new WebSocket(ws_url);
ws.onmessage = onMessage;
ws.onclose = onClose;
}
function onMessage(e) {
if (e.data === "reload") {
location.reload();
}
}
function onClose(e) {
console.warn("WebSocket disconnected from server. Reconnecting in 10 seconds.");
setTimeout(connect, 10000)
}
</script>
]]></description>
<pubDate>Sat, 18 Nov 23 00:51:39 +0000</pubDate>
<link>https://jimfl.github.io/everything-burns/posts/2022-12-29-cable-management-hack.html</link>
<guid>https://jimfl.github.io/everything-burns/posts/2022-12-29-cable-management-hack.html</guid>
</item>
<item>
<title>Obligatory Colophon</title>
<description><![CDATA[
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>Obligatory Colophon - Everything Burns</title>
<link rel="stylesheet" href="/everything-burns/assets/css/style.css">
<link rel="stylesheet" href="/everything-burns/assets/css/prism.css">
<link rel="alternate" type="application/rss+xml" title="Feed for Everything Burns" href="https://jimfl.github.io/everything-burns/rss.xml" />
</head>
<body>
<div id="wrapper">
<header id="top">
<h1><a href="/everything-burns/">Everything Burns</a></h1>
<p>Re-ignited</p>
</header>
<nav>
<ul>
<li><a href="/everything-burns/index.html">Home</a></li>
<li><a href="/everything-burns/posts">Posts</a></li>
<li>Mastodon: <a rel="me" href="https://hachyderm.io/@jimfl">@jimfl@hachyderm.io</a></li>
</ul>
</nav>
<hr>
<main><article class="post"><header><h1 id="obligatory-colophon">Obligatory Colophon</h1><p>
Wednesday, 21 Dec 2022
<span class="tags">
Tags:
<a class="tag" href="/everything-burns/tags/elixir">elixir</a><a class="tag" href="/everything-burns/tags/meta">meta</a></span></p></header><div><p>A few notes on history and how this site is put together. </p><p>In 2001, I discovered and embraced the weblog phenomenon, and maintained a personal blog, running on a
dusty linux box under my desk, likely in violation of my DSL terms of service. I ran that blog until 2005,
when ne’er-do-wells exploited a zero-day vulnerability in the wiki software I was running on the same box,
and trashed the system. “Certainly, you had backups,” you might be asking. Well, allow me to retort. I did
not. So, I moved on with my life, and embraced Twitter instead, leaving that platform in 2016 for obvious
reasons.</p><p>That blog, Everything Burns, is still more or less archived on archive.org. Here are some screenshots of the
header over time:</p><p><img src="/everything-burns/media/old-eb/eb1.png" alt="Original Everything Burns header"/><img src="/everything-burns/media/old-eb/eb2.png" alt="Original Everything Burns header"/><img src="/everything-burns/media/old-eb/eb3.png" alt="Original Everything Burns header"/></p><p>When I retired from software development, I decided that I wanted to have a place to write again, but on
my terms.</p><ul><li>Not self-hosted, but I have full control of the content in neutral format, like Markdown.
</li><li>Movable. I can re-host if needed.
</li><li>Customizable, so that I can add features if need be
</li><li>Supports blog posts and other non-blog type content (plain pages)
</li></ul><p>In terms of hosting, I don’t really want to have a personal domain. I settled on github pages as the hosting
solution, and started looking for a suitable, extensible static website generation tool. <a href="https://jekyllrb.com/">Jekyll</a>
seemed pretty powerful and theme-able, but I rejected it out of hand because Ruby. If I was going to
reject a solution based on language, I thought I might search for a solution based on language, and
started looking for an Elixir-based static site generator. I found <a href="https://dalgona.github.io/Serum/">Serum</a>,
which should look familiar.</p><p>Even though serum supports themes, I haven’t yet been arsed to change from the default theme. This is a contrast
from the previous incarnation when I would spend hours editing MovableType templates and piddling around in
The Gimp. I did end up adding RSS support, via the event hooks facility, and that was really straightforward.</p><p>The workflow for creating a post involves using a <code class="inline">mix</code> task that comes with serum:</p><pre><code class="">$ mix serum.gen.blog --title "Obligatory Colophon" --output obligatory-colophon</code></pre><p>Then editing the resulting Markdown file (I am using the <a href="https://helix-editor.com/">Helix</a> editor, which
may deserve a separate post in itself.)</p><pre><code class="">---
title: Obligatory Colophon
date: 2022-12-21 09:05:48
tags: meta
---
A few notes on history and how this site is put together. </code></pre><p>I can view the whole site locally as I edit with another <code class="inline">mix</code> task that runs a server on localhost.</p><p>To publish, I just </p><pre><code class="">$ mix serum.build
$ cd site
$ git add .
$ git commit -m "I wrote a thing"
$ git push origin main </code></pre><p>and a few minutes later the post appears and is in the RSS feed.</p><p>I can operate this setup from any desktop operating system. Presently I am using MacOS. </p><p>And yes, I have backups.</p></div></article></main>
<hr>
<footer>
<!--=====================================-->
<!-- Override "includes/footer.html.eex" -->
<!-- to customize your footer contents. -->
<!--=====================================-->
<p>
Powered by <a href="https://github.com/Dalgona/Serum">Serum</a> v1.5.1,
with <a href="https://github.com/Dalgona/serum-theme-essence">Essence</a> theme
</p>
</footer>
</div>
<script src="/everything-burns/assets/js/prism.js"></script>
</body>
</html>
<script>
const ws_url = "ws://" + location.host + "/serum_live_reloader";
var ws;
connect();
function connect() {
ws = new WebSocket(ws_url);
ws.onmessage = onMessage;
ws.onclose = onClose;
}
function onMessage(e) {
if (e.data === "reload") {
location.reload();
}
}
function onClose(e) {
console.warn("WebSocket disconnected from server. Reconnecting in 10 seconds.");
setTimeout(connect, 10000)
}
</script>
]]></description>
<pubDate>Sat, 18 Nov 23 00:51:39 +0000</pubDate>
<link>https://jimfl.github.io/everything-burns/posts/2022-12-21-obligatory-colophon.html</link>
<guid>https://jimfl.github.io/everything-burns/posts/2022-12-21-obligatory-colophon.html</guid>
</item>
<item>
<title>Signing Is Not Trivial</title>
<description><![CDATA[
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>Signing Is Not Trivial - Everything Burns</title>
<link rel="stylesheet" href="/everything-burns/assets/css/style.css">
<link rel="stylesheet" href="/everything-burns/assets/css/prism.css">
<link rel="alternate" type="application/rss+xml" title="Feed for Everything Burns" href="https://jimfl.github.io/everything-burns/rss.xml" />
</head>
<body>
<div id="wrapper">
<header id="top">
<h1><a href="/everything-burns/">Everything Burns</a></h1>
<p>Re-ignited</p>
</header>
<nav>
<ul>
<li><a href="/everything-burns/index.html">Home</a></li>
<li><a href="/everything-burns/posts">Posts</a></li>
<li>Mastodon: <a rel="me" href="https://hachyderm.io/@jimfl">@jimfl@hachyderm.io</a></li>
</ul>
</nav>
<hr>
<main><article class="post"><header><h1 id="signing-is-not-trivial">Signing Is Not Trivial</h1><p>
Thursday, 15 Dec 2022
<span class="tags">
Tags:
<a class="tag" href="/everything-burns/tags/cryptography">cryptography</a><a class="tag" href="/everything-burns/tags/signing">signing</a><a class="tag" href="/everything-burns/tags/systems">systems</a></span></p></header><div><p>I hear something akin to the following a lot: “This problem
is simply solved by (cryptographic) signing.”</p><p>Over the last half-decade, I’ve always responded in the moment with a
laundry list of all of the things around signing that need to be built at
whatever scale (corporate, community, Internet), so I decided to collect
my thoughts about it here, so I can reference it later.</p><p>What follows is not really about cryptography at all. I would apologize in
advance for this, but you really don’t want me writing about cryptography. My
point of view is as builder, not cryptographer. Taking cryptographic signatures
as a primitive, it talks about what kinds of things are needed build robust,
usable systems that use signing to provide authenticity, integrity, and
authorization controls.</p><h2 id="cryptographic-signatures">Cryptographic Signatures</h2><p>Of course, modulo the mathematics involved, and ensuring the cryptographic
implementations are coded soundly, signing things and verifying the signatures
is extraordinarily simple. If I have a public-key cryptosystem key pair of some
sort, I can use the private part of the key—known only to me—to produce a
signature over the cryptographic digest of some data (any old bag of bits).
Having shared my public key, anyone who can retrieve the public key is able to
verify that the signature was produced with the private part of the key. What
could be simpler?</p><p>I suspect this is the mental model of most who assert that “this problem is
simply solved by signing.” And it’s more or less correct. But it is far
from complete. Let’s explore what other problems need to be solved in support
of signing, especially if the problem is many-to-many and/or at scale.</p><h2 id="private-key,-known-only-to-me">Private key, Known only to me</h2><p>Most every cryptographic signature mechanism depends upon the security of
the private key. The private key is a chunk of binary data which needs to be
present at the time, and in the environment of signing. In the simple model
above, disclosure of the key allows an attacker to produce signatures that
appear to be valid and come from the legitimate owner of the key. Since the
key is just data which can be duplicated <em>ad infinitum</em> without diminishing
any single copy, there is nothing to make it obvious either to the key owner or
other stakeholders that this has happened.</p><p>So the very first “feature” that we owe in a secure signing system is the
security of the signing environment. What makes this particularly challenging
is that we usually want to make our system available to a diverse set of
users using various operating systems and other tools. If the generation and
management of the key material is commonly left up to the users of the system,
we may need to consider building protections against the legitimate users of
the system, because it isn’t always possible to govern how key material is
managed.</p><p>Uncertainty about the safety of key material is one of the reasons you may see
keys that expire after a certain period of time, limiting the blast radius of
a key exposure. However, if you want to consider a signature to be valid after
the key that produced it has expired, you need to embed authenticated time
information into the signature, which involves more keys and signatures, and
compounds every other problem laid out in what follows. Also, if the security
of your system depends on knowing what time it is, you’ve got more problems.</p><p>One key security strategy is hardware cryptography modules, which at least make
it more obvious when the user no longer has control over the key material. Another
is single-use keys which are created upon a proper authentication by the user
into a centralized system, and where the keys or signatures are countersigned
by that system. In both of these strategies, the signing user needn’t even be exposed to
the key material in order to mishandle it. </p><h2 id="verifying-signatures">Verifying Signatures</h2><p>In order to verify a signature, I need to obtain the public key of the signer.
When it’s just Alice and Bob, this is straightforward. In a larger many-party
system, some mechanism of discovering public key information is needed, not
merely as a convenience, but as a security measure; an attacker might fool
me into thinking something is signed by a legitimate party, by replacing the
public key I am using for verification with a false one.</p><p>A common strategy for this is to provide a directory of public key information,
called a key server. Verifying parties will need to trust this key server
to provide the legitimate key material. The key-server strategy requires the
verifying environment to have connectivity, which is not always viable (think
IoT devices or air-gapped environments).</p><p>Public key certificates (PKI) are often a way to manage key material in such
a way that signatures can be verified in air-gapped environments. The public
key material is sent along with the signature, and is signed by a chain of
certificates signed by one or more root certificates, which are pre-loaded into
the verification environment. As long as the signature chains all the way to
a root that you trust, you have some assurance that the correct public key is
present for the stated identity of the signer.</p><p>Depending upon what is at stake, (per the threat model of the system. You do
have a threat model, right?), any strategy for distributing public key material
must authenticate users before they can publish key material to the key
server. For example, paying for the key-server entry with a credit card (like
you might do for your Apple developer account), or, in the case of web-server
certificates, demonstrating that you have control over a domain by placing an
entry in that domain’s DNS records, or placing some data in a web page in that
domain.</p><p>Once the secure distribution of public keys is solved, there is still the fact
that verification is done in diverse environments, where it is not possible
to govern the security and hygiene of the public key material. If an attacker
is able to manipulate local stores of public key material, they can trick a
verifier into trusting a signature which they should not. PKI certificate trust
stores are an example of such an attack vector. </p><h2 id="authorization">Authorization</h2><p>The foregoing section was primarily about making sure you have an authentic
public key with which to verify that an identity produced a signature, and now
you can verify the signature. That’s essentially an authentication operation.
The next question I might want to answer is: why do I believe that the
authenticated identity was the right one to have produced this signature. This
depends on the semantics of the signature. If it’s an email from Alice to Bob,
Bob can visually verify that the signature was produced by Alice.</p><p>Consider a situation where it is repeatedly proposed that signing is a
solution: open source package repositories. It is suggested that many of
the attacks where malicious code is substituted for genuine code in these
repositories could have been prevented by signing the code. For a software
project of any complexity, the dependency tree may comprise hundreds or
thousands of packages from multiple repositories. Not only would obtaining
the public key material and checking all of those signatures be cumbersome,
something is needed to help the verifier understand that the signatures were
produced by those authorized to do so (i.e. the package maintainers). As far
as I am aware, highly ramified authorization like this is an unsolved problem,
but that may be due to my prejudice against trying to apply blockchain to every
problem I happen across.</p><h2 id="when-things-go-wrong">When Things Go Wrong</h2><p>Of course, all of the above is just getting the happy path to work. Keys will
get compromised. Individuals will lose trust and need to be de-authourized
to produce signatures for some or all of the capabilities granted by their
ability to sign. Registries and key-servers will have defects. And so, systems
need to be in place to manage the key material and other configurations, and
the mechanisms need to be understood by systems running in signing environments
as well as verification environments.</p><h3 id="blast-radius">Blast radius</h3><p>Some of the protective mechanisms can be somewhat automatic. By constraining
the scope of how and when key material can be used, compromises can self-heal
after some time, or only expose a subset of the problem space.</p><p>The easiest example to draw on is the validity period of PKI certificates.
Certificates are chained in hierarchies from a heavily protected root, down
to the leaf-node certificates given to unwashed masses to use. These leaf
certificates often are valid for very short periods of time (hundreds of
days) compared to the roots (decades), so if one is compromised it will stop
being a problem eventually. Using PKI certificates to sign digital assets is
complicated by this, because I generally want the signature on my asset to be
valid beyond the validity period of the key itself (meaning I cannot produce
any more signatures with that key, but I want continue to trust signatures that
were produced while the key itself was valid). Using PKI for signing digital
assets can use a technique called timestamping, where a trusted third party
countersigns a signature with an authenticated timestamp, attesting that the
signature was submitted to the timestamper while the key was valid.</p><p>Expiration dates on certificates cause a lot of problems. It is said that
everyone has a cryptographically signed timestamp for when their website or
service will stop working, embedded in a certificate somewhere. Most people get
a certificate then stop worrying about it until 180 days later when stuff stops
working. They scramble about, get another certificate, then stop worrying for
another 180 days. Even though the glue on sticky notes continues to improve,
this is a perennial problem.</p><p>It’s even worse when a custodian of public key infrastructure (like a
certificate authority, ISP, or cloud provider) has an accidental expiration.
The blast radius of these events can take half the internet down.</p><h3 id="explicit-controls">Explicit controls</h3><p>Often, blast radius measures aren’t enough, and a key needs to be revoked
immediately. This requires a communications channel between the key
distribution mechanism and verifying systems (which may cache keys), once again
requiring connectivity in order to make sure you have the most up-to-date
information about which keys are valid and which are not.</p><h3 id="expect-signature-verification-to-fail">Expect signature verification to fail</h3><p>Another thing that can be overlooked is what happens when a signature fails to