-
Notifications
You must be signed in to change notification settings - Fork 0
/
feed.json
756 lines (702 loc) · 220 KB
/
feed.json
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
{
"version": "https://jsonfeed.org/version/1",
"title": "Benjamin Wang",
"icon": "https://micro.blog/Benny/avatar.jpg",
"home_page_url": "https://micro.benji.ltd/",
"feed_url": "https://micro.benji.ltd/feed.json",
"items": [
{
"id": "https://benny.micro.blog/2024/05/29/overhear-the-conversation.html",
"content_html": "<p>Overhear the conversation of my girlfriend with her parents overseas, the reality hits me again since I don’t have my dad around me anymore, the pressure starts to suffocate me.\nFind a job, buy a house, pay the loan, nine to five.\nAt the end of the day, everything shall be fine, right?</p>\n",
"content_text": "Overhear the conversation of my girlfriend with her parents overseas, the reality hits me again since I don't have my dad around me anymore, the pressure starts to suffocate me.\r\nFind a job, buy a house, pay the loan, nine to five.\r\nAt the end of the day, everything shall be fine, right?\n",
"date_published": "2024-05-29T21:00:21+10:00",
"url": "https://micro.benji.ltd/2024/05/29/overhear-the-conversation.html"
},
{
"id": "https://benny.micro.blog/2024/05/23/n-my-quest.html",
"title": "My Journey Through Note-Taking Apps: Why I Chose OneNote",
"content_html": "<p>In my quest for the perfect note-taking application, I’ve explored a myriad of options, each with its unique features and quirks. From the minimalist Simplenote to the powerful Logseq, from the flexible Obsidian to the all-encompassing Evernote, I’ve tried them all. Each app had its strengths, but none managed to tick all the boxes for me. After all my trials, I found myself coming back to OneNote. Here’s why.</p>\n<h2 id=\"the-long-road-to-onenote\">The Long Road to OneNote</h2>\n<p>Logseq was the app I used the longest. Its powerful outliner and backlinking features were a boon for organizing thoughts. But as my notes grew beyond 150, with around 70 assets, the app started lagging. The whiteboard drawing performance was subpar, and frequent crashes during note-taking sessions were frustrating. This instability made it difficult to rely on Logseq for my daily needs.</p>\n<p>I dabbled with other apps like Obsidian, NotesHub, Saber, CherryTree, Apple Notes, Google Keep, and Evernote. Each had its merits, but none could seamlessly integrate typed and hand-drawn notes on a single page. They either lacked the flexibility I needed or couldn’t handle the volume of notes I worked with.\nWhy OneNote?</p>\n<p>OneNote stood out for a few key reasons. Unlike other apps, OneNote allows me to have typed notes working closely with hand-drawn notes, all on one page. I can easily import PDF printouts or PowerPoint slides and annotate them directly. This feature is invaluable for my workflow, enabling me to integrate different types of content smoothly.</p>\n<p>Even after importing around 1,000 notes, OneNote continued to function as expected—snappy and efficient. The performance remained robust, with no lag or crashes, making it a reliable tool for both work and personal use. The seamless synchronization across devices ensures that my notes are always up-to-date, whether I’m on my computer, tablet, or phone.</p>\n<p>The only downside? It’s owned by Microsoft. While some may view this as a drawback due to privacy concerns or a preference for open-source solutions, the reliability and functionality of OneNote outweigh this for me.</p>\n<h2 id=\"conclusion\">Conclusion</h2>\n<p>While no note-taking app is perfect, OneNote has proven to be the most reliable and versatile option for my needs. Its robust performance, organizational capabilities, and cross-platform support make it the best choice for me, even without the advanced backlinking features found in other apps. If you’re on a similar quest for the perfect note-taking app, I highly recommend giving OneNote a try.</p>\n",
"content_text": "In my quest for the perfect note-taking application, I've explored a myriad of options, each with its unique features and quirks. From the minimalist Simplenote to the powerful Logseq, from the flexible Obsidian to the all-encompassing Evernote, I've tried them all. Each app had its strengths, but none managed to tick all the boxes for me. After all my trials, I found myself coming back to OneNote. Here's why.\n\n## The Long Road to OneNote\n\nLogseq was the app I used the longest. Its powerful outliner and backlinking features were a boon for organizing thoughts. But as my notes grew beyond 150, with around 70 assets, the app started lagging. The whiteboard drawing performance was subpar, and frequent crashes during note-taking sessions were frustrating. This instability made it difficult to rely on Logseq for my daily needs.\n\nI dabbled with other apps like Obsidian, NotesHub, Saber, CherryTree, Apple Notes, Google Keep, and Evernote. Each had its merits, but none could seamlessly integrate typed and hand-drawn notes on a single page. They either lacked the flexibility I needed or couldn't handle the volume of notes I worked with.\nWhy OneNote?\n\nOneNote stood out for a few key reasons. Unlike other apps, OneNote allows me to have typed notes working closely with hand-drawn notes, all on one page. I can easily import PDF printouts or PowerPoint slides and annotate them directly. This feature is invaluable for my workflow, enabling me to integrate different types of content smoothly.\n\nEven after importing around 1,000 notes, OneNote continued to function as expected—snappy and efficient. The performance remained robust, with no lag or crashes, making it a reliable tool for both work and personal use. The seamless synchronization across devices ensures that my notes are always up-to-date, whether I'm on my computer, tablet, or phone.\n\nThe only downside? It's owned by Microsoft. While some may view this as a drawback due to privacy concerns or a preference for open-source solutions, the reliability and functionality of OneNote outweigh this for me.\n\n## Conclusion\n\nWhile no note-taking app is perfect, OneNote has proven to be the most reliable and versatile option for my needs. Its robust performance, organizational capabilities, and cross-platform support make it the best choice for me, even without the advanced backlinking features found in other apps. If you're on a similar quest for the perfect note-taking app, I highly recommend giving OneNote a try.\n",
"date_published": "2024-05-23T16:38:39+10:00",
"url": "https://micro.benji.ltd/2024/05/23/n-my-quest.html",
"tags": ["Posts"]
},
{
"id": "https://benny.micro.blog/2024/04/29/from-what-i.html",
"content_html": "<p>From what I can recall, the VAIO had the best keybaord for laptops hands down.</p>\n",
"content_text": "From what I can recall, the VAIO had the best keybaord for laptops hands down. \n",
"date_published": "2024-04-29T10:14:07+10:00",
"url": "https://micro.benji.ltd/2024/04/29/from-what-i.html"
},
{
"id": "https://benny.micro.blog/2024/04/29/i-forgot-to.html",
"content_html": "<p>I forgot to cancel my ghost blog subscription and I am now have another year of ghost pro…</p>\n",
"content_text": "I forgot to cancel my ghost blog subscription and I am now have another year of ghost pro...\n",
"date_published": "2024-04-29T00:27:19+10:00",
"url": "https://micro.benji.ltd/2024/04/29/i-forgot-to.html"
},
{
"id": "https://benny.micro.blog/2024/03/11/this-is-mad.html",
"content_html": "<p>This is mad fun. 🎮</p>\n<p><!-- raw HTML omitted --><!-- raw HTML omitted --></p>\n",
"content_text": "This is mad fun. 🎮\n\n<video controls=\"controls\" playsinline=\"playsinline\" src=\"https://micro.benji.ltd/uploads/2024/image.mp4\" width=\"640\" height=\"360\" poster=\"https://micro.benji.ltd/uploads/2024/34aef3a040.png\" preload=\"none\"></video>\n",
"date_published": "2024-03-11T22:58:40+10:00",
"url": "https://micro.benji.ltd/2024/03/11/this-is-mad.html"
},
{
"id": "https://benny.micro.blog/2024/03/11/a-drone-journey.html",
"title": "A Drone Journey Begins. ",
"content_html": "<p>As I scrolled through the endless reels on social media, I found myself captivated by the breathtaking footage captured by drones. The bird’s-eye view of stunning landscapes, the heart-pounding chase of drift cars, and the exhilarating pursuit of mountain bike rides all ignited a spark within me. It was at that moment that a thought crystallized in my mind: I wanted to learn how to fly a drone and experience the world through that unique perspective.</p>\n<p>But this desire was more than just a passing fancy. Deep within my heart, I carried a bittersweet memory of my father, who had always dreamed of owning a drone. Unfortunately, due to his health reasons, we never had the chance to make that dream a reality before he passed away. This unfulfilled wish became a driving force behind my newfound passion for drones, fueling my determination to learn and master the skill in his honor.</p>\n<p>To begin my journey, I decided to start with a simulation before venturing into the real world. I purchased the liftoff drone simulation for my Xbox 🎮 and eagerly dove into the virtual skies. Armed with a controller, I embarked on my first attempts at flying, quickly realizing that it was not as easy as the professionals made it seem.</p>\n<p>The initial hours were filled with countless crashes and frustrating moments. I watched helplessly as my virtual drone collided with obstacles and plummeted to the ground time and time again. However, with each failure, I grew more determined to improve. I studied tutorials, watched experienced pilots, and practiced relentlessly.</p>\n<p>After two days of dedicated practice, I finally achieved a small but significant milestone: I completed an entire lap without crashing once. The sense of accomplishment that washed over me was indescribable. It was a testament to the power of perseverance and the joy of learning something new.</p>\n<p>As I continue to hone my skills in the virtual world, I can’t help but feel a profound connection to my father. Every successful maneuver, every smooth landing, feels like a tribute to his memory. I know that he would have been proud to see me pursue this passion with such dedication and enthusiasm.</p>\n<p>Learning to fly a drone has become more than just a hobby for me; it has become a way to honor my father’s legacy and to carry his dream forward. With each flight, whether in the simulation or, eventually, in the real world, I feel a sense of closeness to him, as if he is right there beside me, guiding my movements and sharing in the breathtaking views.</p>\n<p>This journey has taught me that it’s never too late to pursue a dream, even if it originates from the most unexpected places. It has shown me the power of dedication, the value of patience, and the joy of learning. As I continue to progress in my drone piloting skills, I am filled with excitement for the adventures that await me, both in the virtual skies and, one day, in the vast expanse of the real world.</p>\n",
"content_text": "As I scrolled through the endless reels on social media, I found myself captivated by the breathtaking footage captured by drones. The bird's-eye view of stunning landscapes, the heart-pounding chase of drift cars, and the exhilarating pursuit of mountain bike rides all ignited a spark within me. It was at that moment that a thought crystallized in my mind: I wanted to learn how to fly a drone and experience the world through that unique perspective.\n\nBut this desire was more than just a passing fancy. Deep within my heart, I carried a bittersweet memory of my father, who had always dreamed of owning a drone. Unfortunately, due to his health reasons, we never had the chance to make that dream a reality before he passed away. This unfulfilled wish became a driving force behind my newfound passion for drones, fueling my determination to learn and master the skill in his honor.\n\nTo begin my journey, I decided to start with a simulation before venturing into the real world. I purchased the liftoff drone simulation for my Xbox 🎮 and eagerly dove into the virtual skies. Armed with a controller, I embarked on my first attempts at flying, quickly realizing that it was not as easy as the professionals made it seem.\n\nThe initial hours were filled with countless crashes and frustrating moments. I watched helplessly as my virtual drone collided with obstacles and plummeted to the ground time and time again. However, with each failure, I grew more determined to improve. I studied tutorials, watched experienced pilots, and practiced relentlessly.\n\nAfter two days of dedicated practice, I finally achieved a small but significant milestone: I completed an entire lap without crashing once. The sense of accomplishment that washed over me was indescribable. It was a testament to the power of perseverance and the joy of learning something new.\n\nAs I continue to hone my skills in the virtual world, I can't help but feel a profound connection to my father. Every successful maneuver, every smooth landing, feels like a tribute to his memory. I know that he would have been proud to see me pursue this passion with such dedication and enthusiasm.\n\nLearning to fly a drone has become more than just a hobby for me; it has become a way to honor my father's legacy and to carry his dream forward. With each flight, whether in the simulation or, eventually, in the real world, I feel a sense of closeness to him, as if he is right there beside me, guiding my movements and sharing in the breathtaking views.\n\nThis journey has taught me that it's never too late to pursue a dream, even if it originates from the most unexpected places. It has shown me the power of dedication, the value of patience, and the joy of learning. As I continue to progress in my drone piloting skills, I am filled with excitement for the adventures that await me, both in the virtual skies and, one day, in the vast expanse of the real world. \n",
"date_published": "2024-03-11T22:51:46+10:00",
"url": "https://micro.benji.ltd/2024/03/11/a-drone-journey.html"
},
{
"id": "https://benny.micro.blog/2024/03/07/tidal-is-dropping.html",
"content_html": "<p>Tidal is dropping their subscription fee.</p>\n<p>what’s wrong with Kanye?\u2028</p>\n",
"content_text": "Tidal is dropping their subscription fee.\n\nwhat's wrong with Kanye?\u2028\n",
"date_published": "2024-03-07T12:12:02+10:00",
"url": "https://micro.benji.ltd/2024/03/07/tidal-is-dropping.html"
},
{
"id": "https://benny.micro.blog/2024/03/06/now-its-time.html",
"content_html": "<p>Now it’s time to buy 🚀</p>\n",
"content_text": "Now it’s time to buy 🚀\n",
"date_published": "2024-03-06T10:19:37+10:00",
"url": "https://micro.benji.ltd/2024/03/06/now-its-time.html"
},
{
"id": "https://benny.micro.blog/2024/03/04/now-start-selling.html",
"content_html": "<p>Now start selling.</p>\n",
"content_text": "Now start selling. \n",
"date_published": "2024-03-04T13:01:03+10:00",
"url": "https://micro.benji.ltd/2024/03/04/now-start-selling.html"
},
{
"id": "https://benny.micro.blog/2024/03/04/i-keep-trying.html",
"content_html": "<p>I keep trying to find new note taking system other than logseq because the template and properties are making this too barebone, however I keep coming back, even switch to roam research to figure it out it just a simpler version of logseq but with better UI. 📝</p>\n",
"content_text": "I keep trying to find new note taking system other than logseq because the template and properties are making this too barebone, however I keep coming back, even switch to roam research to figure it out it just a simpler version of logseq but with better UI. 📝\n",
"date_published": "2024-03-04T00:50:11+10:00",
"url": "https://micro.benji.ltd/2024/03/04/i-keep-trying.html"
},
{
"id": "https://benny.micro.blog/2024/03/01/im-pretty-much.html",
"content_html": "<p>I’m pretty much ready to get the steam deck, and I realize there’s now an OLED version. Wow, that totally changes everything!</p>\n",
"content_text": "I'm pretty much ready to get the steam deck, and I realize there's now an OLED version. Wow, that totally changes everything!\n",
"date_published": "2024-03-01T04:47:07+10:00",
"url": "https://micro.benji.ltd/2024/03/01/im-pretty-much.html"
},
{
"id": "https://benny.micro.blog/2024/03/01/samsung-s-ultra.html",
"content_html": "<p>Samsung S24 Ultra with Osu! Lazer is amazing. It brings back the high school memories of playing Osu! ouendan on 3DS. 🎮</p>\n",
"content_text": "Samsung S24 Ultra with Osu! Lazer is amazing. It brings back the high school memories of playing Osu! ouendan on 3DS. 🎮\n",
"date_published": "2024-03-01T04:45:23+10:00",
"url": "https://micro.benji.ltd/2024/03/01/samsung-s-ultra.html"
},
{
"id": "https://benny.micro.blog/2024/03/01/so-this-is.html",
"content_html": "<p>So this is what false fire alarm feels like in 3:30 in the morning. As usual, the building manager is nowhere to be found.</p>\n",
"content_text": "So this is what false fire alarm feels like in 3:30 in the morning. As usual, the building manager is nowhere to be found. \n",
"date_published": "2024-03-01T04:14:01+10:00",
"url": "https://micro.benji.ltd/2024/03/01/so-this-is.html"
},
{
"id": "https://benny.micro.blog/2024/02/29/a-frustrating-encounter.html",
"title": "A Frustrating Encounter: Screen Protector Incident on the Gold Coast",
"content_html": "<p>A few days ago, I found myself strolling through the bustling streets of the Gold Coast, basking in the sun-kissed ambiance of this coastal paradise. Little did I know, my leisurely escapade was about to take a frustrating turn. You see, I had recently acquired a brand-new phone, and like any responsible gadget owner, I decided it was time to safeguard its pristine screen with a protector.With this mission in mind, I ventured into a local phone shop in the mall, confident that I would soon be walking out with my phone’s screen securely shielded.</p>\n<p>The shop assistant recommended an UV-cured tempered screen protector, assuring me of its superior protection and durability. The price tag? A steep fifty dollars. But, believing in the adage “you get what you pay for,” I agreed.</p>\n<p>The process seemed straightforward enough, until the assistant decided to give my phone’s S Pen a similar UV treatment. At the time, I didn’t think much of it. However, this decision would soon come back to haunt me. In the days that followed, I noticed the paint on the S Pen starting to peel off, as if the UV liquid had waged war against the plastic, leaving it battered and bruised.</p>\n<p>But the ordeal didn’t end there. To my dismay, I soon discovered that the edges of the screen protector were already showing signs of wear and tear, with small chips appearing like unwelcome guests at a party. And let’s not forget the S Pen, which had transformed from a sleek accessory to a half-broken tool, its button now stubbornly resistant to my clicks.Here I am, barely two weeks into owning my new phone, and I’m already dealing with a chipped screen protector and a compromised S Pen.</p>\n<p>It’s a stark reminder that sometimes, even the best-laid plans can go awry. As I reflect on this experience, I can’t help but feel a mix of frustration and resignation. After all, it’s just one of those things that happen, right? But rest assured, next time I’ll be treading a bit more cautiously when it comes to protecting my gadgets.</p>\n",
"content_text": "A few days ago, I found myself strolling through the bustling streets of the Gold Coast, basking in the sun-kissed ambiance of this coastal paradise. Little did I know, my leisurely escapade was about to take a frustrating turn. You see, I had recently acquired a brand-new phone, and like any responsible gadget owner, I decided it was time to safeguard its pristine screen with a protector.With this mission in mind, I ventured into a local phone shop in the mall, confident that I would soon be walking out with my phone's screen securely shielded.\n\n The shop assistant recommended an UV-cured tempered screen protector, assuring me of its superior protection and durability. The price tag? A steep fifty dollars. But, believing in the adage \"you get what you pay for,\" I agreed.\n\nThe process seemed straightforward enough, until the assistant decided to give my phone's S Pen a similar UV treatment. At the time, I didn't think much of it. However, this decision would soon come back to haunt me. In the days that followed, I noticed the paint on the S Pen starting to peel off, as if the UV liquid had waged war against the plastic, leaving it battered and bruised.\n\nBut the ordeal didn't end there. To my dismay, I soon discovered that the edges of the screen protector were already showing signs of wear and tear, with small chips appearing like unwelcome guests at a party. And let's not forget the S Pen, which had transformed from a sleek accessory to a half-broken tool, its button now stubbornly resistant to my clicks.Here I am, barely two weeks into owning my new phone, and I'm already dealing with a chipped screen protector and a compromised S Pen.\n\n It's a stark reminder that sometimes, even the best-laid plans can go awry. As I reflect on this experience, I can't help but feel a mix of frustration and resignation. After all, it's just one of those things that happen, right? But rest assured, next time I'll be treading a bit more cautiously when it comes to protecting my gadgets.\n",
"date_published": "2024-02-29T23:43:20+10:00",
"url": "https://micro.benji.ltd/2024/02/29/a-frustrating-encounter.html"
},
{
"id": "https://benny.micro.blog/2024/02/29/this-is-a.html",
"content_html": "<p>This is a weird feeling, after I’m slowly switching my main phone to android, I cannot seems to stop picking it up. Weird behaviour I developed since I tend to not use phone as much as I tend to when I was using iPhones.</p>\n",
"content_text": "This is a weird feeling, after I'm slowly switching my main phone to android, I cannot seems to stop picking it up. Weird behaviour I developed since I tend to not use phone as much as I tend to when I was using iPhones.\n",
"date_published": "2024-02-29T23:29:08+10:00",
"url": "https://micro.benji.ltd/2024/02/29/this-is-a.html"
},
{
"id": "https://benny.micro.blog/2024/02/26/being-using-microblog.html",
"content_html": "<p>Being using micro.blog for awhile now, still gets confused when I want to post some thought to topics. Which emotions to use</p>\n",
"content_text": "Being using micro.blog for awhile now, still gets confused when I want to post some thought to topics. Which emotions to use\n",
"date_published": "2024-02-26T17:03:54+10:00",
"url": "https://micro.benji.ltd/2024/02/26/being-using-microblog.html"
},
{
"id": "https://benny.micro.blog/2024/02/26/as-an-asiian.html",
"content_html": "<p>As an asiian dude who went to another English country for a degree. Every random I met on the road looked like a movie character I saw in the movie when I was a child. Shit is trippy as hell</p>\n",
"content_text": "As an asiian dude who went to another English country for a degree. Every random I met on the road looked like a movie character I saw in the movie when I was a child. Shit is trippy as hell\n",
"date_published": "2024-02-26T17:02:42+10:00",
"url": "https://micro.benji.ltd/2024/02/26/as-an-asiian.html"
},
{
"id": "https://benny.micro.blog/2024/02/22/temperarory-disable-samsung.html",
"title": "Temperarory disable Samsung Keyboard",
"content_html": "<p>So, recently I got me a S24 Ultra because my iPhone 14 Pro keeps acting weird on its cellular chip and went for Apple Genius couple of times for nothing fixed.\nOnce I started to use the Samsung Dex I fidget it around alot, I noticed that the Samsung Keyboard is only keyboard being used for the Dex. However, for the Chinese input method I’ve always use ShuangPin, which is not supported on Samsung Keyboard. In order to let Samsung Dex choose Gboard over Samsung Keyboard the only method is remove the application to make Samsung Dex falls back to second keyboard. And here is how you do it.</p>\n<ol>\n<li>\n<p>Disable Auto Blocker in Samsung’s Security settings to enable USB debugging.</p>\n</li>\n<li>\n<p>Then, activate Developer Mode and USB debugging in Developer Options.</p>\n</li>\n<li>\n<p>Install ADB on your computer to connect and manage your device.</p>\n</li>\n<li>\n<p>Remove Samsung Keyboard with <code>pm uninstall -k --user 0 com.samsung.android.honeyboard</code>.</p>\n</li>\n</ol>\n<p>After doing this your Samsung Dex should pick up the Gboard. However this will cause the Accessibility settings not usable in the Settings app, and may affect complex lock screen passwords since android only uses non-third party keyboard for input.</p>\n<p>So, to restore Samsung Keyboard, use Samsung Store to redownload the package or run the command using adb <code>pm install-existing com.samsung.android.honeyboard</code></p>\n",
"content_text": "So, recently I got me a S24 Ultra because my iPhone 14 Pro keeps acting weird on its cellular chip and went for Apple Genius couple of times for nothing fixed.\nOnce I started to use the Samsung Dex I fidget it around alot, I noticed that the Samsung Keyboard is only keyboard being used for the Dex. However, for the Chinese input method I've always use ShuangPin, which is not supported on Samsung Keyboard. In order to let Samsung Dex choose Gboard over Samsung Keyboard the only method is remove the application to make Samsung Dex falls back to second keyboard. And here is how you do it.\n\n1. Disable Auto Blocker in Samsung's Security settings to enable USB debugging. \n\n2. Then, activate Developer Mode and USB debugging in Developer Options. \n\n3. Install ADB on your computer to connect and manage your device. \n\n4. Remove Samsung Keyboard with `pm uninstall -k --user 0 com.samsung.android.honeyboard`. \n\nAfter doing this your Samsung Dex should pick up the Gboard. However this will cause the Accessibility settings not usable in the Settings app, and may affect complex lock screen passwords since android only uses non-third party keyboard for input.\n\nSo, to restore Samsung Keyboard, use Samsung Store to redownload the package or run the command using adb `pm install-existing com.samsung.android.honeyboard`\n",
"date_published": "2024-02-22T11:58:38+10:00",
"url": "https://micro.benji.ltd/2024/02/22/temperarory-disable-samsung.html"
},
{
"id": "https://benny.micro.blog/2024/02/13/the-cellular-problem.html",
"content_html": "<p>The cellular problem I had on my iPhone led me to switch my daily device to the newly bought Samsung slowly (haven’t crashed once since I bought it). Now, my MacBook Pro has decided that the iMessage is worthless and is not letting me log in anymore.\nWell, #fuckyou #apple .</p>\n",
"content_text": "The cellular problem I had on my iPhone led me to switch my daily device to the newly bought Samsung slowly (haven't crashed once since I bought it). Now, my MacBook Pro has decided that the iMessage is worthless and is not letting me log in anymore. \nWell, #fuckyou #apple .\n",
"date_published": "2024-02-13T21:33:41+10:00",
"url": "https://micro.benji.ltd/2024/02/13/the-cellular-problem.html"
},
{
"id": "https://benny.micro.blog/2024/02/06/ecommerce-on-blockchain.html",
"content_html": "<p>E-commerce on blockchain with smart contract without any regulations. Hmm.\nAlmost finished.</p>\n",
"content_text": "E-commerce on blockchain with smart contract without any regulations. Hmm. \nAlmost finished. \n",
"date_published": "2024-02-06T13:51:04+10:00",
"url": "https://micro.benji.ltd/2024/02/06/ecommerce-on-blockchain.html"
},
{
"id": "https://benny.micro.blog/2023/12/20/yesterday-i-had.html",
"content_html": "<p>Yesterday, I had an order to pack and deliver two tubs of Oreo-flavored ice cream along with a 24-pack of 350ml Coke cans. Upon reaching the delivery location, I was unable to find the customer, Mr. C. Despite waiting for 15 minutes and trying to contact him through messages and calls, which were redirected to voicemail, I couldn’t complete the delivery as it required a PIN code.\nI contacted support and had the delivery canceled. Surprisingly, Mr. C still generously tipped me almost half the cost of the order. I wanted to let Mr. C know, in case he comes across this post, that I ended up giving the Coke cans to homeless individuals near the CBD area. Given the hot weather, I kept the ice cream, as it wouldn’t have been practical to distribute it.</p>\n",
"content_text": "Yesterday, I had an order to pack and deliver two tubs of Oreo-flavored ice cream along with a 24-pack of 350ml Coke cans. Upon reaching the delivery location, I was unable to find the customer, Mr. C. Despite waiting for 15 minutes and trying to contact him through messages and calls, which were redirected to voicemail, I couldn't complete the delivery as it required a PIN code.\nI contacted support and had the delivery canceled. Surprisingly, Mr. C still generously tipped me almost half the cost of the order. I wanted to let Mr. C know, in case he comes across this post, that I ended up giving the Coke cans to homeless individuals near the CBD area. Given the hot weather, I kept the ice cream, as it wouldn't have been practical to distribute it.\n",
"date_published": "2023-12-20T07:07:58+10:00",
"url": "https://micro.benji.ltd/2023/12/20/yesterday-i-had.html"
},
{
"id": "https://benny.micro.blog/2023/12/14/so-the-way.html",
"content_html": "<p>So the way Apple replied to beeper is to add Key Transparency and Contact Key to the iMessage, neat. <em>wink</em></p>\n",
"content_text": "So the way Apple replied to beeper is to add Key Transparency and Contact Key to the iMessage, neat. *wink*\n",
"date_published": "2023-12-14T13:28:02+10:00",
"url": "https://micro.benji.ltd/2023/12/14/so-the-way.html"
},
{
"id": "https://benny.micro.blog/2023/12/02/not-gonna-lie.html",
"content_html": "<p>Not gonna lie Elon’s statement to advertisers are good enough for me to consider the premium of X.</p>\n",
"content_text": "Not gonna lie Elon’s statement to advertisers are good enough for me to consider the premium of X. \n",
"date_published": "2023-12-02T08:09:03+10:00",
"url": "https://micro.benji.ltd/2023/12/02/not-gonna-lie.html"
},
{
"id": "https://benny.micro.blog/2023/11/23/i-guess-today.html",
"content_html": "<p>I guess today was a good day.</p>\n",
"content_text": "I guess today was a good day. \n",
"date_published": "2023-11-23T16:42:56+10:00",
"url": "https://micro.benji.ltd/2023/11/23/i-guess-today.html"
},
{
"id": "https://benny.micro.blog/2023/11/12/semester-is-end.html",
"content_html": "<p>Semester is end, I’ve done what I can.</p>\n",
"content_text": "Semester is end, I've done what I can. \n",
"date_published": "2023-11-12T17:26:37+10:00",
"url": "https://micro.benji.ltd/2023/11/12/semester-is-end.html"
},
{
"id": "https://benny.micro.blog/2023/11/08/httpswwwyoutubecomwatchvrlrbtxrmc-optus-outage.html",
"content_html": "<p><a href=\"https://www.youtube.com/watch?v=rLrBt1XrM0c\">www.youtube.com/watch</a>\n#optus #outage</p>\n<p>I’ve been taunt and laughed about the choice of my cellular choice of Telstra because Optus is soo much cheaper atm. I never doubt the price comes with the reliability.</p>\n<p>Look who’s laughing now.</p>\n",
"content_text": "[www.youtube.com/watch](https://www.youtube.com/watch?v=rLrBt1XrM0c)\n#optus #outage \n\nI've been taunt and laughed about the choice of my cellular choice of Telstra because Optus is soo much cheaper atm. I never doubt the price comes with the reliability.\n\nLook who's laughing now.\n",
"date_published": "2023-11-08T09:57:40+10:00",
"url": "https://micro.benji.ltd/2023/11/08/httpswwwyoutubecomwatchvrlrbtxrmc-optus-outage.html"
},
{
"id": "https://benny.micro.blog/2023/11/08/oh-optus-fucked.html",
"content_html": "<p>Oh Optus fucked.</p>\n",
"content_text": "Oh Optus fucked.\n",
"date_published": "2023-11-08T09:04:04+10:00",
"url": "https://micro.benji.ltd/2023/11/08/oh-optus-fucked.html"
},
{
"id": "https://benny.micro.blog/2023/11/07/oh-gpt-just.html",
"content_html": "<p>Oh GPT just got 2 year worth of knowledge update.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "Oh GPT just got 2 year worth of knowledge update.\n\n<img src=\"uploads/2023/241988a159.png\" width=\"600\" height=\"212\" alt=\"\">\n",
"date_published": "2023-11-07T12:00:27+10:00",
"url": "https://micro.benji.ltd/2023/11/07/oh-gpt-just.html"
},
{
"id": "https://benny.micro.blog/2023/11/03/yep-i-broke.html",
"content_html": "<p>Yep I broke the gpt.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "Yep I broke the gpt.\n\n<img src=\"uploads/2023/03f1aadd7a.png\" width=\"600\" height=\"316\" alt=\"\">\n",
"date_published": "2023-11-03T15:11:02+10:00",
"url": "https://micro.benji.ltd/2023/11/03/yep-i-broke.html"
},
{
"id": "https://benny.micro.blog/2023/11/02/i-can-tell.html",
"content_html": "<p>I can tell that nowadays, many people are trying to reinvent the wheel.</p>\n",
"content_text": "I can tell that nowadays, many people are trying to reinvent the wheel.\n",
"date_published": "2023-11-02T14:05:37+10:00",
"url": "https://micro.benji.ltd/2023/11/02/i-can-tell.html"
},
{
"id": "https://benny.micro.blog/2023/10/31/kerlock-homes-an.html",
"content_html": "<h1 id=\"kerlock-homes---an-innovative-program-might-protect-the-kernel\">Kerlock Homes - An innovative program might protect the Kernel.</h1>\n<p>Based on a recent research of mine on eBPF. I’ve created a program called Kerlock Homes, which aim to use multiple eBPF program to protect the attacker using eBPF to hide the trace of the attack.\n<a href=\"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/p3nj/KerlockHomes\">github.com/p3nj/Kerl…</a></p>\n",
"content_text": "# Kerlock Homes - An innovative program might protect the Kernel.\nBased on a recent research of mine on eBPF. I've created a program called Kerlock Homes, which aim to use multiple eBPF program to protect the attacker using eBPF to hide the trace of the attack. \n[github.com/p3nj/Kerl...](https://github.com/p3nj/KerlockHomes)\n",
"date_published": "2023-10-31T10:55:05+10:00",
"url": "https://micro.benji.ltd/2023/10/31/kerlock-homes-an.html"
},
{
"id": "https://benny.micro.blog/2023/10/16/sometimes-i-think.html",
"content_html": "<p>Sometimes, I think it’s impossible to be happy or me not trying harder.</p>\n",
"content_text": "Sometimes, I think it's impossible to be happy or me not trying harder.\n",
"date_published": "2023-10-16T11:22:49+10:00",
"url": "https://micro.benji.ltd/2023/10/16/sometimes-i-think.html"
},
{
"id": "https://benny.micro.blog/2023/10/12/its-good-to.html",
"content_html": "<p>It’s good to know about the dark side of the network. Makes you self-aware.</p>\n",
"content_text": "It's good to know about the dark side of the network. Makes you self-aware.\n",
"date_published": "2023-10-12T11:23:26+10:00",
"url": "https://micro.benji.ltd/2023/10/12/its-good-to.html"
},
{
"id": "https://benny.micro.blog/2023/10/12/ironically-the-students.html",
"content_html": "<p>Ironically, the students are not coming to lecture in the last few weeks of the semester.</p>\n",
"content_text": "Ironically, the students are not coming to lecture in the last few weeks of the semester.\n",
"date_published": "2023-10-12T11:04:18+10:00",
"url": "https://micro.benji.ltd/2023/10/12/ironically-the-students.html"
},
{
"id": "https://benny.micro.blog/2023/10/10/israels-siege-on.html",
"title": " Israel's Siege on Gaza: The Humanitarian Crisis Unfolding",
"content_html": "<h3 id=\"introduction\">Introduction:</h3>\n<p>As the conflict between Israel and Gaza intensifies, the residents of Gaza are facing a dire humanitarian crisis. Israel’s recent decision to cut off food, water, and power to Gaza has resulted in devastating consequences. In this blog post, we will delve into the details of the ongoing conflict, the impact on innocent civilians, and the urgent need for international intervention.</p>\n<h3 id=\"air-strikes-devastate-gaza\">Air strikes devastate Gaza</h3>\n<p>The Israeli Army’s air strikes have left Gaza in ruins, with many buildings destroyed and countless lives lost. The indiscriminate bombings have targeted densely populated areas, causing immense suffering for innocent civilians.</p>\n<h3 id=\"efforts-to-help-the-injured\">Efforts to help the injured</h3>\n<p>Despite the challenging circumstances, individuals like Amit, a brave paramedic, have risked their lives to help the injured. Their selfless actions shine a light of hope amidst the darkness.</p>\n<h3 id=\"israel-battling-militants-infiltrating-from-gaza\">Israel battling militants infiltrating from Gaza</h3>\n<p>Israel’s military has been in a fierce battle with militants infiltrating from Gaza. Police stations have been taken over by Hamas, leading to ongoing clashes between militants and soldiers.</p>\n<h3 id=\"rockets-fired-from-gaza-cause-panic\">Rockets fired from Gaza cause panic</h3>\n<p>As tensions escalate, rockets fired from Gaza have caused panic among Israeli citizens. Cities across Israel have been struck, and the streets in some areas have been deserted due to the imminent danger.</p>\n<h3 id=\"the-toll-on-innocent-lives\">The toll on innocent lives</h3>\n<p>Innocent people on both sides of the conflict are paying the price for the actions of a few. The loss of lives and the devastation caused are painful reminders of the lasting impact of violence.</p>\n<h3 id=\"desperate-pleas-for-help\">Desperate pleas for help</h3>\n<p>Amidst the chaos, families and individuals trapped in Gaza desperately need military and police help. The uncertainty and fear for their safety are palpable in their heartfelt messages.</p>\n<h3 id=\"the-need-for-international-intervention\">The need for international intervention</h3>\n<p>The international community must create a ceasefire to negotiate a settlement. The situation in Gaza is rapidly deteriorating, and immediate action is required to prevent further loss of lives.</p>\n<h3 id=\"conclusion\">Conclusion:</h3>\n<p>The ongoing conflict between Israel and Gaza has resulted in a dire humanitarian crisis. Innocent civilians bear the brunt of the violence, and urgent intervention is needed to mitigate the suffering. The international community must work towards a peaceful resolution and protect innocent lives.</p>\n",
"content_text": "### Introduction:\n\nAs the conflict between Israel and Gaza intensifies, the residents of Gaza are facing a dire humanitarian crisis. Israel's recent decision to cut off food, water, and power to Gaza has resulted in devastating consequences. In this blog post, we will delve into the details of the ongoing conflict, the impact on innocent civilians, and the urgent need for international intervention.\n\n\n### Air strikes devastate Gaza\n\n The Israeli Army's air strikes have left Gaza in ruins, with many buildings destroyed and countless lives lost. The indiscriminate bombings have targeted densely populated areas, causing immense suffering for innocent civilians.\n\n### Efforts to help the injured\n\n Despite the challenging circumstances, individuals like Amit, a brave paramedic, have risked their lives to help the injured. Their selfless actions shine a light of hope amidst the darkness.\n\n### Israel battling militants infiltrating from Gaza\n\n Israel's military has been in a fierce battle with militants infiltrating from Gaza. Police stations have been taken over by Hamas, leading to ongoing clashes between militants and soldiers.\n\n### Rockets fired from Gaza cause panic\n\n As tensions escalate, rockets fired from Gaza have caused panic among Israeli citizens. Cities across Israel have been struck, and the streets in some areas have been deserted due to the imminent danger.\n\n### The toll on innocent lives\n\n Innocent people on both sides of the conflict are paying the price for the actions of a few. The loss of lives and the devastation caused are painful reminders of the lasting impact of violence.\n\n### Desperate pleas for help\n\n Amidst the chaos, families and individuals trapped in Gaza desperately need military and police help. The uncertainty and fear for their safety are palpable in their heartfelt messages.\n\n### The need for international intervention\n\n The international community must create a ceasefire to negotiate a settlement. The situation in Gaza is rapidly deteriorating, and immediate action is required to prevent further loss of lives.\n\n\n### Conclusion:\n\nThe ongoing conflict between Israel and Gaza has resulted in a dire humanitarian crisis. Innocent civilians bear the brunt of the violence, and urgent intervention is needed to mitigate the suffering. The international community must work towards a peaceful resolution and protect innocent lives.\n",
"date_published": "2023-10-10T12:00:26+10:00",
"url": "https://micro.benji.ltd/2023/10/10/israels-siege-on.html"
},
{
"id": "https://benny.micro.blog/2023/10/10/israels-cut-off.html",
"content_html": "<p>Israel’s cut off of food, water, and power to Gaza has caused devastation and loss of lives. Innocent people are suffering as a result. The situation is expected to worsen. #GazaCrisis</p>\n",
"content_text": "Israel's cut off of food, water, and power to Gaza has caused devastation and loss of lives. Innocent people are suffering as a result. The situation is expected to worsen. #GazaCrisis\n",
"date_published": "2023-10-10T11:58:15+10:00",
"url": "https://micro.benji.ltd/2023/10/10/israels-cut-off.html"
},
{
"id": "https://benny.micro.blog/2023/10/09/i-dont-know.html",
"content_html": "<p>I don’t know when this happens, but chatGPT reenables the Bing functionality.</p>\n",
"content_text": "I don't know when this happens, but chatGPT reenables the Bing functionality.\n",
"date_published": "2023-10-09T09:44:41+10:00",
"url": "https://micro.benji.ltd/2023/10/09/i-dont-know.html"
},
{
"id": "https://benny.micro.blog/2023/10/07/one-car-driving.html",
"content_html": "<p>One car driving like an unlicensed underage kid ruins your whole mood.</p>\n",
"content_text": "One car driving like an unlicensed underage kid ruins your whole mood.\n",
"date_published": "2023-10-07T15:53:51+10:00",
"url": "https://micro.benji.ltd/2023/10/07/one-car-driving.html"
},
{
"id": "https://benny.micro.blog/2023/10/07/ironically-flip-five.html",
"content_html": "<p>Ironically flip five doesn’t have any reception problem, but on the other hand, iPhone be like a giant turd hitting a jet engine.\nThanks to Apple, after ten years of the ecosystem but you still screwed up with the fundamental functionality of a PHONE.</p>\n",
"content_text": "Ironically flip five doesn't have any reception problem, but on the other hand, iPhone be like a giant turd hitting a jet engine. \nThanks to Apple, after ten years of the ecosystem but you still screwed up with the fundamental functionality of a PHONE. \n",
"date_published": "2023-10-07T09:23:11+10:00",
"url": "https://micro.benji.ltd/2023/10/07/ironically-flip-five.html"
},
{
"id": "https://benny.micro.blog/2023/10/07/still-has-the.html",
"content_html": "<p>17.0.3 still has the esim issue. I have been have to carry my flip5 with me daily because my iPhone constantly loses carrier signal.</p>\n<p>Get your shit together, Apple.</p>\n",
"content_text": "17.0.3 still has the esim issue. I have been have to carry my flip5 with me daily because my iPhone constantly loses carrier signal. \n\nGet your shit together, Apple. \n",
"date_published": "2023-10-07T09:20:45+10:00",
"url": "https://micro.benji.ltd/2023/10/07/still-has-the.html"
},
{
"id": "https://benny.micro.blog/2023/10/06/who-ever-trained.html",
"content_html": "<p>Who ever trained GPT to do this deserves a medal.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "Who ever trained GPT to do this deserves a medal.\n\n\n<img src=\"uploads/2023/87183ad4ac.png\" width=\"498\" height=\"204\" alt=\"\">\n",
"date_published": "2023-10-06T09:13:03+10:00",
"url": "https://micro.benji.ltd/2023/10/06/who-ever-trained.html"
},
{
"id": "https://benny.micro.blog/2023/10/03/oh-well-glad.html",
"content_html": "<p>Oh well, glad we had this semester break.</p>\n",
"content_text": "Oh well, glad we had this semester break.\n",
"date_published": "2023-10-03T07:36:30+10:00",
"url": "https://micro.benji.ltd/2023/10/03/oh-well-glad.html"
},
{
"id": "https://benny.micro.blog/2023/09/19/macaron-is-a.html",
"content_html": "<p>Macaron is a pretty good tool for protecting artefacts during the CI/CD, loved to see its future achievements. Great job from the Oracle team</p>\n",
"content_text": "Macaron is a pretty good tool for protecting artefacts during the CI/CD, loved to see its future achievements. Great job from the Oracle team \n",
"date_published": "2023-09-19T11:46:37+10:00",
"url": "https://micro.benji.ltd/2023/09/19/macaron-is-a.html"
},
{
"id": "https://benny.micro.blog/2023/09/17/glad-i-have.html",
"content_html": "<p>Glad I have my flip 5 with me, Apple really need to fix their shit. This is one of the main reason I haven’t preorder new iPhone that I’ve been doing for last 10 plus years.</p>\n",
"content_text": "Glad I have my flip 5 with me, Apple really need to fix their shit. This is one of the main reason I haven't preorder new iPhone that I've been doing for last 10 plus years. \n",
"date_published": "2023-09-17T11:43:27+10:00",
"url": "https://micro.benji.ltd/2023/09/17/glad-i-have.html"
},
{
"id": "https://benny.micro.blog/2023/09/17/im-not-gonna.html",
"content_html": "<p>I’m not gonna lie this freaking eSIM carrier shit with ios17 is driving me nuts.</p>\n<!-- raw HTML omitted -->\n",
"content_text": "I’m not gonna lie this freaking eSIM carrier shit with ios17 is driving me nuts.\n\n<img src=\"uploads/2023/image.png\" width=\"276\" height=\"600\" alt=\"\">\n",
"date_published": "2023-09-17T10:52:44+10:00",
"url": "https://micro.benji.ltd/2023/09/17/im-not-gonna.html"
},
{
"id": "https://benny.micro.blog/2023/09/16/ill-never-will.html",
"content_html": "<p>I’ll never will be as good as you in badminton.\nBut I will try hard.</p>\n",
"content_text": "I’ll never will be as good as you in badminton. \nBut I will try hard. \n",
"date_published": "2023-09-16T20:05:24+10:00",
"url": "https://micro.benji.ltd/2023/09/16/ill-never-will.html"
},
{
"id": "https://benny.micro.blog/2023/09/16/recently-there-have.html",
"content_html": "<p>Recently, there have been a lot of scams being revealed.\nI believe the bubble going to bust again.\u2028</p>\n",
"content_text": "Recently, there have been a lot of scams being revealed.\nI believe the bubble going to bust again.\u2028\n",
"date_published": "2023-09-16T10:10:08+10:00",
"url": "https://micro.benji.ltd/2023/09/16/recently-there-have.html"
},
{
"id": "https://benny.micro.blog/2023/09/15/nothing-beats-the.html",
"content_html": "<p>Nothing beats the feels when you get to play Juicy in the morning, blasting the music out to the neighbourhood because I know today all my neighbours are working today.</p>\n",
"content_text": "Nothing beats the feels when you get to play Juicy in the morning, blasting the music out to the neighbourhood because I know today all my neighbours are working today.\n",
"date_published": "2023-09-15T10:08:50+10:00",
"url": "https://micro.benji.ltd/2023/09/15/nothing-beats-the.html"
},
{
"id": "https://benny.micro.blog/2023/09/14/realise-that-technology.html",
"content_html": "<p>Realise that technology is movin faster than ever recently. Is this rapid pace largely due to the growing popularity of LLM and GPT? While the average user uses AI to simply retrieve answers, those who truly understand how to leverage AI to its fullest potential allow it to foster knowledge linking.</p>\n",
"content_text": "Realise that technology is movin faster than ever recently. Is this rapid pace largely due to the growing popularity of LLM and GPT? While the average user uses AI to simply retrieve answers, those who truly understand how to leverage AI to its fullest potential allow it to foster knowledge linking.\n",
"date_published": "2023-09-14T23:00:28+10:00",
"url": "https://micro.benji.ltd/2023/09/14/realise-that-technology.html",
"tags": ["Fleeting Thoughts"]
},
{
"id": "https://benny.micro.blog/2023/09/14/cant-bother-to.html",
"content_html": "<p>Can’t bother to watch recap even.</p>\n",
"content_text": "Can't bother to watch recap even. \n",
"date_published": "2023-09-14T13:26:25+10:00",
"url": "https://micro.benji.ltd/2023/09/14/cant-bother-to.html"
},
{
"id": "https://benny.micro.blog/2023/09/14/is-it-me.html",
"content_html": "<p>Is it me or the internet connection general in #Australia are sucks lately?</p>\n",
"content_text": "Is it me or the internet connection general in #Australia are sucks lately?\n",
"date_published": "2023-09-14T13:24:51+10:00",
"url": "https://micro.benji.ltd/2023/09/14/is-it-me.html"
},
{
"id": "https://benny.micro.blog/2023/09/14/i-love-how.html",
"content_html": "<p>I love how Apple keep changing their boot logo size.</p>\n",
"content_text": "I love how Apple keep changing their boot logo size. \n",
"date_published": "2023-09-14T12:08:04+10:00",
"url": "https://micro.benji.ltd/2023/09/14/i-love-how.html"
},
{
"id": "https://benny.micro.blog/2023/09/14/memory-flashback.html",
"title": "Memory flashback",
"content_html": "<p>I used to work in a small factory, with good pay, and the owner is a genuine good old mate.</p>\n<p>Around 2016, he lost big money because the brand had shifted orders to factories in southeast Asia and China, but the owner won’t let us lose jobs.\nSo before the new year, we tied up the factory real clean, and all stopped going to work on the first day of the year just to let the owner stop losing his life savings. We went to his house to Barbie, and the accountant and his wife revealed our plan.</p>\n<p>Eventually, they retired, and the owner pulled his connections to ensure most employees had jobs.</p>\n",
"content_text": "I used to work in a small factory, with good pay, and the owner is a genuine good old mate. \n\nAround 2016, he lost big money because the brand had shifted orders to factories in southeast Asia and China, but the owner won’t let us lose jobs. \nSo before the new year, we tied up the factory real clean, and all stopped going to work on the first day of the year just to let the owner stop losing his life savings. We went to his house to Barbie, and the accountant and his wife revealed our plan. \n\nEventually, they retired, and the owner pulled his connections to ensure most employees had jobs. \n",
"date_published": "2023-09-14T08:28:55+10:00",
"url": "https://micro.benji.ltd/2023/09/14/memory-flashback.html"
},
{
"id": "https://benny.micro.blog/2023/09/12/it-scares-me.html",
"content_html": "<p>It scares me that my first instinct now is to ask GPT instead of searching on Google.</p>\n",
"content_text": "It scares me that my first instinct now is to ask GPT instead of searching on Google.\n",
"date_published": "2023-09-12T10:33:39+10:00",
"url": "https://micro.benji.ltd/2023/09/12/it-scares-me.html"
},
{
"id": "https://benny.micro.blog/2023/09/08/my-mate-told.html",
"content_html": "<p>My mate told me the pizza and bbq flavor of Shapes are the best, I just finished the chicken one. It’s really addictively good.<br>\nMaybe pizza next🤔</p>\n",
"content_text": "My mate told me the pizza and bbq flavor of Shapes are the best, I just finished the chicken one. It’s really addictively good. \nMaybe pizza next🤔\n",
"date_published": "2023-09-08T09:27:27+10:00",
"url": "https://micro.benji.ltd/2023/09/08/my-mate-told.html"
},
{
"id": "https://benny.micro.blog/2023/09/07/got-two-shapes.html",
"content_html": "<p>Got two shapes from the convi life is gooooood</p>\n",
"content_text": "Got two shapes from the convi life is gooooood\n",
"date_published": "2023-09-07T18:20:17+10:00",
"url": "https://micro.benji.ltd/2023/09/07/got-two-shapes.html"
},
{
"id": "https://benny.micro.blog/2023/09/07/i-accidentally-found.html",
"content_html": "<p>I accidentally found some old gem on the internet.\nThe blog posts I left on another platform. I already forgot I had those posts and was planning to writ it for a long time.\nI guess I will start doing the job that I left off.</p>\n",
"content_text": "I accidentally found some old gem on the internet. \nThe blog posts I left on another platform. I already forgot I had those posts and was planning to writ it for a long time. \nI guess I will start doing the job that I left off. \n",
"date_published": "2023-09-07T15:45:13+10:00",
"url": "https://micro.benji.ltd/2023/09/07/i-accidentally-found.html"
},
{
"id": "https://benny.micro.blog/2023/09/06/im-so-glad.html",
"content_html": "<p>I’m so glad my resource project got my professor’s attention, more emails and zoom meetings yay 🥲</p>\n",
"content_text": "I'm so glad my resource project got my professor's attention, more emails and zoom meetings yay 🥲\n",
"date_published": "2023-09-06T23:43:15+10:00",
"url": "https://micro.benji.ltd/2023/09/06/im-so-glad.html"
},
{
"id": "https://benny.micro.blog/2023/09/05/slowing-moving-from.html",
"content_html": "<p>Slowing moving from Ghost to Micro.blog,\nNowadays, a blog needs to subscribe with a hundred bucks for a year is a little bit too much for a small tech blog I hosted.</p>\n",
"content_text": "Slowing moving from Ghost to Micro.blog,\r\nNowadays, a blog needs to subscribe with a hundred bucks for a year is a little bit too much for a small tech blog I hosted. \n",
"date_published": "2023-09-05T14:29:41+10:00",
"url": "https://micro.benji.ltd/2023/09/05/slowing-moving-from.html"
},
{
"id": "https://benny.micro.blog/2023/04/16/the-neverending-struggle.html",
"title": "The Never-Ending Struggle of Connecting an External Monitor to a Linux Laptop with a Dedicated Nvidia GPU",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>As a Linux user, I was excited to upgrade my laptop to a new model with a dedicated Nvidia GPU. However, I soon discovered that using an external monitor with DisplayPort (DP) was a nightmare. While HDMI connected via the laptop's integrated GPU worked out of the box with minor adjustments, the DP simply refused to work correctly. I spent countless hours trying to debug the issue and make it work, but all my attempts failed.</p><p>Firstly, I checked the hardware and ensured the DP cable and the monitor were functioning properly. I also tried connecting the laptop to different monitors, but the problem persisted. Next, I checked the Nvidia drivers and ensured they were installed and updated. However, the DP still did not work even after updating the drivers.</p><p>I then dug deeper into the issue and found that the problem was how Nvidia GPUs handled DP. I discovered that the Nvidia GPU was not powering on the DP output when the laptop booted up, which caused the external monitor not to detect any signal. This issue was not unique to my laptop, and many other Linux users had reported the same problem with Nvidia GPUs and DP.</p><p>After researching the issue, I found that some workarounds involved using external adapters or modifying the <code>xorg.conf</code> file. However, these solutions were not ideal as they often resulted in other issues, such as screen flickering or compatibility issues like waking up from suspend/hibernate. Furthermore, using an external adapter defeated the purpose of having a DP port on the laptop in the first place.</p><p>Eventually, I decided to ditch the DP for the time being and stick with using HDMI via the laptop's integrated GPU. While it was not ideal, it was a reliable solution that worked without glitches or issues. However, I continued to keep an eye on the issue and hoped that a proper fix would be available in the future.</p><p>In conclusion, using an external monitor with DP on Linux laptops with dedicated Nvidia GPUs can be a nightmare. Despite numerous attempts to debug the issue, I could not make it work without experiencing glitches or issues. While workarounds such as using external adapters or modifying configuration files may work for some users, they were not suitable solutions for me. Ultimately, I decided to ditch the DP and use HDMI instead.</p>\n",
"date_published": "2023-04-16T08:05:02+10:00",
"url": "https://micro.benji.ltd/2023/04/16/the-neverending-struggle.html"
},
{
"id": "https://benny.micro.blog/2023/04/08/dynamic-wallpaper-with.html",
"title": "Dynamic wallpaper with pywal",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>Let your linux have dynamic wallpaper just like Apple did to their macOS.</p><p>Dynamic wallpapers are a great way to personalize your desktop and add some variety to your computer experience. Pywal is a popular tool that allows you to generate colour schemes based on the wallpaper you're using. However, if you want to take it a step further and have a dynamic wallpaper that changes throughout the day, you can use dwall, a tool built on top of Pywal that changes your wallpaper and colour scheme based on the time of day.</p><p>Dwall is a dynamic wallpaper manager for Linux that integrates with pywal. Dwall allows you to use multiple wallpapers that change automatically based on various factors. In this article, we will learn how to set up dynamic wallpaper using pywal and dwall.</p><p>Here's how you can set up dwall on your Linux system:</p><h2 id=\"install-pywal-and-feh\">Install Pywal and feh:</h2><pre><code class=\"language-bash\">$ sudo apt-get install python3-pywal feh</code></pre><h2 id=\"clone-the-dwall-repository\">Clone the dwall repository:</h2><pre><code class=\"language-bash\">$ git clone https://github.com/mczachurski/dynamic-wallpaper.git</code></pre><p>Go into the <code>dynamic-wallpaper</code> directory and run the <code>install.sh</code> script:</p><h2 id=\"install-dwall\">Install dwall</h2><pre><code class=\"language-bash\">$ cd dynamic-wallpaper \n$ ./install.sh</code></pre><p>This will install dwall and set it up to run as a systemd service. You can customize the wallpapers and colour schemes that dwall uses by editing the <code>config.ini</code> file in the <code>dynamic-wallpaper</code> directory.</p><h2 id=\"setup-crontab-job\">Setup crontab job</h2><p>Once dwall is installed, you need to set up a cron job to run the script every minute. Open your crontab file by running the following command:</p><pre><code class=\"language-bash\">$ crontab -e</code></pre><p>If this is your first time running <code>crontab</code>, you will be prompted to select an editor.</p><ol><li>Add the following line to the crontab file:</li></ol><pre><code class=\"language-bash\">* * * * * env TERM=xterm DISPLAY=:0 DESKTOP_SESSION=<your desktop session> DBUS_SESSION_BUS_ADDRESS=<your dbus address> /path/to/dwall -s <theme>\n</code></pre><p>Replace <code>/path/to/dynamic-wallpaper</code> with the path to the directory where you cloned the dwall repository.</p><p>This command sets the necessary environment variables (<code>TERM</code>, <code>DISPLAY</code>, <code>DESKTOP_SESSION</code>, and <code>DBUS_SESSION_BUS_ADDRESS</code>) and runs the <code>dwall.py</code> script every minute.</p><p>This will ensure that dwall starts running automatically every time you log in to your system.</p><p>Save and close the crontab file.</p><p>That's it! You should now have a dynamic wallpaper that changes throughout the day based on the time of day. You can browse all the available themes on the project's <a href=\"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/adi1090x/dynamic-wallpaper\">GitHub</a> page.</p>\n",
"date_published": "2023-04-08T21:40:31+10:00",
"url": "https://micro.benji.ltd/2023/04/08/dynamic-wallpaper-with.html"
},
{
"id": "https://benny.micro.blog/2023/04/06/deploy-redis-on.html",
"title": "Deploy Redis On Kubernetes Using StatefulSet",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>Kubernetes is a powerful container orchestration tool that can be used to deploy and manage containerized applications at scale. One of the many benefits of Kubernetes is its ability to deploy StatefulSets, which are designed to manage stateful applications, such as databases, in a way that ensures data consistency and availability. In this article, we will explore how to deploy Redis on Kubernetes using a StatefulSet.</p><h2 id=\"prerequisites\">Prerequisites</h2><p>Before we dive into the deployment process, there are a few prerequisites that need to be in place. First, you need to have a working Kubernetes cluster. If you don't have one setup already, there are several options available for setting up a Kubernetes cluster, including using a managed Kubernetes service such as Google Kubernetes Engine (GKE), Amazon Elastic Kubernetes Service (EKS) or Microsoft Azure Kubernetes Service (AKS).</p><p>Once you have your Kubernetes cluster set up, you will need to have the <code>kubectl</code> command-line tool installed on your local machine. Kubectl is the primary tool used to interact with Kubernetes clusters and is used to deploy and manage Kubernetes resources.</p><p>Lastly, you will need a Redis container image. You can either use an existing Redis image available on Docker Hub, or you can build your own image if you have specific requirements.</p><h2 id=\"deploying-redis-on-kubernetes\">Deploying Redis on Kubernetes</h2><p>Now that we have our prerequisites in place let's get started with deploying Redis on Kubernetes using a StatefulSet.</p><h3 id=\"step-1-create-a-redis-configuration-file\">Step 1: Create a Redis Configuration File</h3><p>Before deploying Redis using StatefulSet, we need to create a Redis configuration file. This file will contain the configuration parameters that Redis needs to run.</p><pre><code class=\"language-conf\">bind 0.0.0.0\nprotected-mode no\n</code></pre><p>This configuration file sets Redis to bind to all network interfaces and disables the protected mode.</p><h2 id=\"step2-create-a-redis-docker-image\">Step2: Create a Redis Docker Image</h2><p>Now that we have our Redis configuration file, it's time to create a Docker image that includes the configuration file. We'll use a Dockerfile to build the image.</p><p>Create a file named Dockerfile with the following contents:</p><pre><code class=\"language-Dockerfile\">FROM redis:latest\nCOPY redis.conf /usr/local/etc/redis/redis.conf\nCMD [ \"redis-server\", \"/usr/local/etc/redis/redis.conf\" ]\n</code></pre><p>This Dockerfile sets the base image to the latest version of Redis, copies our Redis configuration file into the image, and sets the CMD to start Redis with the configuration file.</p><p>Build the Docker image by running the following command:</p><pre><code class=\"language-bash\">$ docker build -t my-redis-image .</code></pre><h2 id=\"step-3-create-a-redis-service\">Step 3: Create a Redis Service</h2><p>Now that we have our Redis Docker image, it's time to create a Kubernetes Service that will expose Redis to the rest of the cluster.</p><p>Create a file named <code>redis-service.yaml</code> with the following contents:</p><pre><code class=\"language-yaml\">apiVersion: v1\nkind: Service\nmetadata:\n name: redis\nspec:\n selector:\n app: redis\n ports:\n - name: redis\n port: 6379\n\t</code></pre><p>This Kubernetes Service will expose port 6379, which is the default port used by Redis.</p><p>Create the Service by running the following command:</p><pre><code class=\"language-bash\">$ kubectl apply -f redis-service.yaml</code></pre><h2 id=\"step-4-create-a-persistent-volume-claim-pvc\">Step 4: Create a Persistent Volume Claim (PVC)</h2><p>To store Redis data on Kubernetes, we need to create a Persistent Volume Claim (PVC) that will be used by the Redis StatefulSet. A PVC requests a specific amount of storage from the Kubernetes cluster, which will be dynamically provisioned by a StorageClass.</p><p>Create a file named <code>redis-pvc.yaml</code> with the following contents:</p><pre><code class=\"language-yaml\">apiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n name: redis-pvc\nspec:\n accessModes:\n - ReadWriteOnce\n resources:\n requests:\n storage: 1Gi\n</code></pre><p>This YAML file defines a PVC named <code>redis-pvc</code> with a request for 1GB of storage. We are requesting the <code>ReadWriteOnce</code> access mode, which means that the PVC can be mounted as read-write by a single node at a time.</p><p>To create the PVC, run the following command:</p><pre><code class=\"language-bash\">$ kubectl apply -f redis-pvc.yaml\n</code></pre><p>Verify that the PVC is created by running the following command:</p><pre><code class=\"language-bash\">$ kubectl get pvc</code></pre><p>You should see an output similar to the following:</p><pre><code class=\"language-bash\">NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE\nredis-pvc Bound pvc-8d7d420b-c4a4-4c4b-8b84-2ce4e0b7c1b1 1Gi RWO standard 10s\n</code></pre><h2 id=\"step-5-create-a-redis-statefulset\">Step 5: Create a Redis StatefulSet</h2><p>Now that we have our Redis Docker image and Kubernetes Service, it's time to create the Redis StatefulSet.</p><p>Create a file named <code>redis-statefulset.yaml</code> with the following contents:</p><pre><code class=\"language-yaml\">apiVersion: apps/v1\nkind: StatefulSet\nmetadata:\n name: redis\nspec:\n serviceName: redis\n replicas: 3\n selector:\n matchLabels:\n app: redis\n template:\n metadata:\n labels:\n app: redis\n spec:\n containers:\n - name: redis\n image: my-redis-image\n imagePullPolicy: IfNotPresent\n ports:\n - containerPort: 6379\n name: redis\n\t volumes:\n - name: redis-data\n \tpersistentVOlumeClaim:\n claimName: redis-pvc\n volumeClaimTemplates:\n - metadata:\n name: redis-pvc\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n</code></pre><p>This StatefulSet specifies that we want to run three replicas of Redis and that we want to use the Redis Service that we created earlier. It also specifies that we want to use the <code>my-redis-image</code> Docker image that we built earlier.</p><pre><code class=\"language-bash\">$ kubectl apply -f redis-statefulset.yaml</code></pre><h2 id=\"connecting-to-the-redis-cluster-and-adding-data\">Connecting to the Redis Cluster and Adding Data</h2><p>Now that we have our Redis deployment running let's connect to the cluster and add some data.</p><p>To connect to the Redis cluster, we first need to find the IP address of one of the pods. We can do this by running the following command:</p><pre><code class=\"language-bash\">$ kubectl get pods -l app=redis</code></pre><p>This will list all of the Redis pods and their status. Copy the name of one of the pods and use it in the following command to get its IP address:</p><pre><code class=\"language-bash\">$ kubectl describe pod <pod-name> | grep IP:</code></pre><p>Replace <code><pod-name></code> with the name of the Redis pod you want to connect to. This will output the IP address of the pod.</p><p>Next, we need to install the Redis command-line interface (CLI) on our local machine. You can do this by running the following command:</p><pre><code class=\"language-bash\">$ sudo apt-get install redis-tools</code></pre><p>or on Arch Linux</p><pre><code class=\"language-bash\">$ sudo pacman -S redis</code></pre><p>Once the Redis CLI is installed, we can connect to the Redis cluster by running the following command:</p><pre><code class=\"language-bash\">$ redis-cli -h <redis-ip> -p 6379</code></pre><p>Replace <code><redis-ip></code> with the IP address of the Redis pod you want to connect to.</p><p>Once you're connected to the Redis cluster, you can add some data by running Redis commands. For example, you can set a key-value pair by running the following command:</p><pre><code class=\"language-redis\">set mykey myvalue</code></pre><p>You can then retrieve the value of the key by running the following command:</p><pre><code class=\"language-redis\">get mykey</code></pre><p>This will output the value of the key, which should be <code>myvalue</code>.</p><h2 id=\"conclusion\">Conclusion</h2><p>In this blog post, we've gone through the steps necessary to deploy Redis on Kubernetes using StatefulSets. We started by setting up a Kubernetes cluster on our local machine and creating a PresistentVolumeClaim to store Redis data. We then created a StatefulSet definition to deploy multiple Redis pods, each with its own unique hostname and persistent storage.</p><p>Finally, we verified that our deployment was successful by connecting to the Redis cluster and adding some data.</p><p>Using StatefulSets to deploy stateful applications like Redis on Kubernetes provides many benefits. It allows us to easily scale our application horizontally while maintaining data consistency and durability. It also simplifies the deployment process by abstracting away the details of managing stateful pods. With this knowledge, you should be able to deploy Redis on Kubernetes and other stateful applications using StatefulSets with confidence.</p><h2 id=\"further-reading\">Further Reading</h2><p>If you're interested in learning more about Kubernetes and stateful applications, here are some additional resources you may find useful:</p><ul><li><a href=\"https://kubernetes.io/docs/home/\">Kubernetes Documentation</a></li><li><a href=\"https://redis.io/documentation\">Redis Documentation</a></li><li><a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/\">Kubernetes StatefulSets</a></li><li><a href=\"https://www.infoq.com/articles/kubernetes-stateful-applications/\">Managing Stateful Applications with Kubernetes</a></li><li><a href=\"https://kubernetes.io/docs/concepts/services-networking/service/\">Kubernetes Networking: Understanding Service Networking</a></li></ul><h2 id=\"references\">References</h2><ul><li><a href=\"https://kubernetes.io/\">Kubernetes</a></li><li><a href=\"https://redis.io/\">Redis</a></li></ul><p>Thank you for following along with me, and happy deploying!</p>\n",
"date_published": "2023-04-06T21:09:08+10:00",
"url": "https://micro.benji.ltd/2023/04/06/deploy-redis-on.html"
},
{
"id": "https://benny.micro.blog/2023/04/06/powering-up-the.html",
"title": "Powering Up: The Pursuit of Power Efficiency in Linux Laptop Applications",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>As someone who carries a gaming laptop around with Linux in it, I often come across laptop users who are constantly on the lookout for lightweight applications that won't drain their batteries too quickly. These users are frequently on the move and can't always access power outlets for long periods of time, so power consumption is a top priority for them. However, many Linux desktop environments don't prioritise power efficiency, leaving laptop users feeling frustrated and searching for better options. I often hear from laptop users who are constantly searching for lightweight applications that won't drain their batteries too quickly. These users are often on the go and don't have access to power outlets for extended periods of time, which makes power consumption a top priority. While there are some great lightweight options out there, many Linux desktop environments are not designed with power efficiency in mind, which can leave laptop users feeling frustrated and in search of better solutions.</p><h2 id=\"why-linux-laptop-users-crave-lightweight-applications\">Why Linux Laptop Users Crave Lightweight Applications</h2><p>The desire for lightweight applications among Linux laptop users is fuelled by the need to optimise battery life. Running resource-intensive applications can quickly deplete a laptop's battery, which can be detrimental for users who rely on their devices while on the move. This is why many users opt for minimalist applications like lightweight text editors and web browsers that are designed to consume less power.</p><p>While these lightweight applications can certainly help to extend battery life on Linux laptops, they may not always provide the most efficient or feature-rich options. Some users find themselves sacrificing functionality and convenience in exchange for a few extra minutes of battery life. This is where lightweight window managers like AwesomeWM or DWM can come in handy.</p><h2 id=\"the-advantages-of-lightweight-window-managers\">The Advantages of Lightweight Window Managers</h2><p>Lightweight window managers are designed to consume fewer system resources than traditional desktop environments like GNOME or KDE. This means that they can provide an efficient and responsive computing experience, even on older or less powerful hardware. They are also highly customisable, making them a favourite among power users who want complete control over their computing experience.</p><p>However, it's important to note that lightweight window managers are not for everyone. They require more technical knowledge to set up and configure than traditional desktop environments, which can be a barrier to entry for some users. Additionally, they may not offer the same level of out-of-the-box functionality and ease of use that many users have come to expect from modern desktop environments.</p><h2 id=\"choosing-the-right-desktop-environment-for-your-needs\">Choosing the Right Desktop Environment for Your Needs</h2><p>For laptop users who prioritise both power and efficiency, a lightweight window manager like AwesomeWM or DWM may be the best choice. These window managers are designed with low power consumption in mind and can provide an efficient and customisable computing experience that can help to extend battery life.</p><p>In conclusion, while lightweight applications can certainly help to extend battery life on Linux laptops, developers need to prioritise power efficiency when designing desktop environments. Lightweight window managers like AwesomeWM and DWM are great options for users who prioritise both power and efficiency, but they may not be the best choice for everyone. Ultimately, the choice of desktop environment or window manager will depend on a user's individual needs and preferences.</p>\n",
"date_published": "2023-04-06T20:13:07+10:00",
"url": "https://micro.benji.ltd/2023/04/06/powering-up-the.html"
},
{
"id": "https://benny.micro.blog/2023/03/30/from-neovim-to.html",
"title": "From Neovim to Doom Emacs: Discovering the Power of org-mode and org-roam",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>As a tech enthusiast and software developer, I'm always on the lookout for new tools and technologies that can help me become more productive and efficient in my work. Recently, I made the switch from Neovim to Doom Emacs, and I have to say, I've been blown away by what I've found.</p><p>For those not familiar, Vim and Emacs are both powerful text editors that have been around for decades. Vim is known for its speed and simplicity, while Emacs is known for its flexibility and extensibility. There's a never-ending debate about which one is better, and I've seen my fair share of internet memes about it.</p><p>But with Doom Emacs, I get the best of both worlds. I get the speed and simplicity of Vim, along with the flexibility and customisation of Emacs. It's been a bit of a learning curve, but it's been well worth it.</p><p>One of the things that have really impressed me about Emacs is its integration with org-mode. Org-mode is a powerful tool that enables you to create and manage notes, to-do lists, and projects all in one place. It's incredibly flexible and customisable, and can be used for everything from writing articles to managing your personal life.</p><p>But what sets org-mode apart is org-roam, a package that adds a graph database to org-mode. With org-roam, you can easily create links between your notes and connect them to a network of ideas. This allows you to navigate your notes quickly, find related information, and build new insights.</p><p>I've been using org-mode and org-roam for a few weeks now, and it's completely transformed the way I work. I no longer have to keep track of notes and to-do lists in multiple places, and everything is in one centralised location. And with org-roam, I can quickly find related information and make connections between my notes.</p><p>In fact, I'm so impressed with org-mode and org-roam that I've decided to start using it for all my writing and note-taking needs. This means that my future blog posts will mostly be scheduled and written using org-mode.</p><p>Now, I know that switching from Neovim to Doom Emacs might not be for everyone. But if you're looking for a powerful and flexible tool to help you manage your notes and ideas, I highly recommend checking out Doom Emacs and giving org-mode a try. Who knows, it might just change the way you work too!</p><p>In conclusion, as someone who's always looking to improve their workflow, making the switch to Doom Emacs and discovering org-mode and org-roam has been a game-changer for me. I'm excited to see where this new journey takes me, and I can't wait to share my experiences with you in future blog posts written with org-mode.</p>\n",
"date_published": "2023-03-30T21:45:45+10:00",
"url": "https://micro.benji.ltd/2023/03/30/from-neovim-to.html"
},
{
"id": "https://benny.micro.blog/2022/12/20/life-log-the.html",
"title": "Life Log #4—The end of an era.",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>It is just like yesterday that I saw you, FaceTime with you, had hugged you. All of a sudden, you are not there anymore. There are no such explanations that can help me to express how I felt about this whole situation. Even though, deep in our minds, we already know this probably will happen. I thought I was ready to accept the truth, but somehow, something still broke, shattered, turned into sand and spread inside my heart.</p><h2 id=\"the-stages-of-grief\">The stages of grief</h2><p>I started to look for any substance that could help me numb everything. But I am not sure I am broken. Whisky? Give me that. Wine? I can have it like drinking OJ. The funny thing is more fucked up I was, the soberer I became the following day. This kind of forces me to face the truth, I now officially have no father. I believe he is up somewhere, looking down at me. He was probably smiling that I successfully passed the bar to apply for the scholarship for my Master’s degree.</p><p>Everyone told me they had a dream about my dad after he passed away, which I had none of. In my culture, loved ones will come to their beloved’s dream to let them know that he is doing well, to show there is nothing for them to worried about. Since my grandpa passed away, the only one I had a dream of, I never dreamt about anyone else after. Even though I try hard to dream of it, it just doesn’t work. Probably something with me is horribly wrong. I cannot grieve properly. I do not know if that is a gift or a curse. The only thing I could do was sink myself into booze and hope I felt something.</p><h2 id=\"adjust\">Adjust?</h2><p>He was a rigorous father he was. Yes, he was. Even after I became an adult and started to be in charge of most of my life, he still treated me like I were a kid. I know this is probably the general thing in Asian culture. However, not for these years before. His health conditions are significantly worsening, and he often suffers from pain and insomnia with liver and kidney failure. He becomes numb. Sometimes I don’t know if he is here or not. There is so much to talk about, but I can put no words into this blog. Right before I went to Australia for my degree, we had a long serious talk about I might not be able to see you after I went back to Taiwan. I still cannot imagine that would open the door, and you are not there cries at me to bring something to you. I hopped on the aeroplane as soon as I submitted my last assignment. It was an overnight flight, and I could not sleep at all. I asked for so much wine to try to fall asleep but failed. All the ceremonies are like a dream. I must detach myself somehow. I cried a little bit. However, that is it. No tears are coming out anymore. If someone says I am very good at moving on, I would not believe you. I am still sad, and I am still heartbroken. I still want to shout and drink myself to half-death. However, life must go on. It does not mean I do not love my father. Even some of the memories are sad and angry. I still love him more than any other person in my life.</p><h2 id=\"i-love-you-every-day-and-now-i-will-miss-you-every-day\">I love you every day. And now I will miss you every day.</h2><p>I love you every day with all my heart. You brought me to this world and food to the table. You make the life I have every single bit of it. You always said that the mother of the family is the centre. For me, you are the one who keeps the family together. Ever since you are gone, this family has been falling apart. No one wants to gather now because you were not there messaging and calling every one of your siblings to come home to grab a bite. You were an essential part of the family. Now things have changed. You always say we have to take care of mom. We will. You are always afraid that mom has no money to live on. We will take care of it. Do not you worried. We got it covered, Dad.</p><p>There is so much more I want to express in this blog post, but so less is being typed here. But life goes on.</p>\n",
"date_published": "2022-12-20T15:21:43+10:00",
"url": "https://micro.benji.ltd/2022/12/20/life-log-the.html"
},
{
"id": "https://benny.micro.blog/2022/09/07/life-log-midterm.html",
"title": "Life Log #3 - mid-term break",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>I know it’s only a couple of hours after my <a href=\"__GHOST_URL__/life-log-2-2/\">Life Log #2</a> was published. Because I never had a chance to finish my draft before I hit the mid-term break.</p><h2 id=\"being-a-student\">Being a student</h2><p>Being a student is fantastic. Let me write down my thoughts about being a student after you work for a few years.</p><p>Usually, people go to work, get off work, go back home and chill for the rest of the day. Maybe have an eat out with friends and family, but that’s it, some people might choose to read in their free time, but I know most of them are just chilling, stopped.</p><p>Being a student, you don’t have to worry about anything—actually, two things to worried about, grades and what is for the next meal. No review nor report is needed by the end of the day, only assignments due at the weekend. Furthermore, the satisfaction of learning new knowledge is unbeatable.</p><h2 id=\"there-is-a-plot-twist\">There is a plot twist</h2><p>However, there is always a but. Studying in a different language could be problematic. In Australia, many second-generation immigrants and international students like me are simultaneously studying in the same major. Communicating has become a significant problem for me. Yet because I learnt most of my English in American English, Aussie / British accents often caught me off guard during the lectures. I have to redo the lecture recording multiple times to understand the whole picture. Luckily I made a few friends who are native Aussie and Singapore. We talk a lot and love each other’s country, so my listening and pronunciation have improved bit by bit.</p><p>Near the mid-term, when we have to finish multiple assignments for four units, I can have a decent conversation with them with no problem.</p><h2 id=\"mid-term-break\">Mid-term break!</h2><p>Unlike another major, Information of Technology has eight units in the first semester. First, we have to finish four units in five weeks, come with two weeks of ‘mid-term breaks’ and continue with the rest of the four units. So lecture has been compressed from thirteen weeks to just five weeks. It is REALLY intense and challenging for non-native speakers and boarding students like me. Luckily QUT has plenty of resources that can be used, many friendly people and classmates willing to help you, no conditionally. Everyone is helping each other in the class. It’s night and day compared to what I experienced in my home country.</p><p>After high pressure intense of five weeks, mid-term breaks are here! However, all of my friends are feeling weird, under extreme pressure for five weeks (well, for me, it is more like three weeks), and suddenly, we do not know what to do. So I come back here and restart a couple of my drafts, and because writing is still my weakness when it comes to assignment writing, I need to practice as much as possible during the mid-term breaks.</p><p>I think that’s the latest update of my journal, and I will see you guys in the next one.</p><p>P.S. Though I know Apple will announce a new model of the MacBook Pro later this year, I still bought a 2021 MacBook Pro last week. It’s fast, lovely, and a fantastic piece of art with a MagSafe AND HDMI port.</p>\n",
"date_published": "2022-09-07T13:51:08+10:00",
"url": "https://micro.benji.ltd/2022/09/07/life-log-midterm.html"
},
{
"id": "https://benny.micro.blog/2022/09/07/life-log.html",
"title": "Life Log #2",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>It has been about a month since I come to Australia. Shit is going crazy and crazier. First of all, I’ve got two offers from QUT information technology. One is starting in July, without EAP, and one is with EAP class and starts in November. The first few days, I’m just chilling, meeting new friends and having fun. All the good dudes are from my home country, so we get along fairly quickly. I believe it’s like the fourth day I am in Australia, and a good friend told me that if I take the offer, which starts in November, I would not be able to have the Christmas holiday with my family. Because the mid-term breaks are only a week-long, Taiwan still has the policy to quarantine seven days after entering the country. So, if I go back to Taiwan and spend some quality Christmas with my family, I will have to take the offer starting in July.</p><h2 id=\"uh-oh-ielts\">Uh oh, IELTS</h2><p>It looks like it will be a-okay if I take the offer. However, I didn’t achieve the IELTS score in the writing section. Which needed to be bend 6; I only had 5.5 at the time. So, either I have to redo the IELTS test within two weeks, or I’m going to say bye-bye to Christmas. That is not going to happen under my watch, I said to myself. I booked my IELTS test for a week later and…. I failed. I am writing section scores of 5.5 again. From now on, I can almost see the Christmas holiday waving away from me. However, a day after, my agent told me that in QUT, they agreed students have a late enrolment before Week 2. This is huge news for me. Soon enough, I booked my second IELTS test; another 395 AUD was gone; from this point, I had already spent 1185 AUD on IELTS, and I could not fail again.</p><h2 id=\"the-comeback\">The comeback</h2><p>From this point, I did not care about EAP. Moreover, I did not care about EAP teaching me how to write my essay, and multiple friends told me it would be intense if I started my Master of Information Technology a week late. I want to return to Taiwan and spend my Christmas holiday with my family. So, I borrowed some writing notes from my roommates and memorised multiple templates for the writing section. Furthermore, I stopped practising all the sections to focus on my writing. Reading? Nah, Listening? Too Easy. Speaking? I will force myself to go out and have a random convo with some Aussie, and I believe I will be fine. After five days of practising. Results come in on a Thursday morning as text messages; I got 7.5 on Listening, both Reading and Writing are 6, and Speaking with 6.5. I PASS THE BAR.</p><h2 id=\"just-getting-started\">Just getting started</h2><p>Days after, I got my result, just like fast-forwarding. I picked up my results at the IDP the same day I got the text message. After I handed my result to my agent, he told me he would take care of everything else, and 2 hours later, a tuition payment link was sitting inside my mailbox. Paid the bill. Ouch. And going to class on Thursday and Friday, I got my student Id; it’s happening.</p><h2 id=\"late-enrolment-is-not-good\">Late enrolment is not good.</h2><p>After I paid my tuition fee, everything started to roll. However, it’s already two weeks gone if you count orientation week. With eight units finished in ten weeks, it’s going to be like shit hits the fan.</p><p>The first of a few weeks is just trying to catch up with the classes, watching lecture recordings almost every night and at weekends, trying to finish all the tutorial questions and doing assignments simultaneously.</p><p>I just do not have time for anything else. Even my side projects are stopped, along with this blog. Drafts are being updated slowly and no posts are coming out, eventually, I decided to stop doing everything and just focus on my study.</p>\n",
"date_published": "2022-09-07T13:08:45+10:00",
"url": "https://micro.benji.ltd/2022/09/07/life-log.html"
},
{
"id": "https://benny.micro.blog/2022/07/20/docker-runtime-without.html",
"title": "Docker Runtime Without Docker Desktop on MacOS",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<h2 id=\"tldr\">TL;DR;</h2><blockquote><a href=\"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/abiosoft/colima\">Colima</a> to the rescue.</blockquote><h2 id=\"docker-desktop-is-bad\">Docker Desktop is bad.</h2><p>It is been awhile I use <code>Docker Desktop</code> , I always use <code>Command-line Interface</code>. Which is <code>CLI</code> for short, to manage my images and containers. I did have <code>Docker Desktop</code> on my M1 Mac while writing this. To be honest, Even though <code>Graphical User Interface</code>, which is short for <code>GUI</code> is more suitable for modern use. However, most of the <code>GUI</code> application have some draw back such as lack of functionality or UI/UX defects.</p><p>Since such reasons, I generally stay away to almost all GUI for my own sanity, Majority of the time I just feel more comfortable when using <code>CLI</code>.</p><p>Also because of this, I didn’t get the news that <a href=\"https://www.servethehome.com/docker-abruptly-starts-charging-many-users-for-docker-desktop/\">Docker Abruptly Starts Charging Many Users for Docker Desktop</a>. Which, is knowledgeable but still absurd to witness a company so desperately to make some money from an app that nobody uses. Well, most of the people including me is using it as a daemon to start <code>dockerd</code>.</p><h2 id=\"can-we-run-dockerd-on-macos-docker-desktop-free\">Can We Run <code>dockerd</code> on macOS Docker Desktop-free?</h2><p>Sure we can, thanks to the open source community, we have plenty of options to run Docker Desktop on M1 macOS for free, and there is more. It will not clog up as much resources as Docker Desktop did. You can terminate <code>dockerd</code> anytime you want. Plus, startup time is way faster than Docker Desktop.</p><h2 id=\"simplist-solution\">Simplist Solution.</h2><p>Because I already went that path. So, you don’t have to go down to that path anymore. After hours of googling and GitHub browsing. I found a project that will achieve all of the requirement that we desire. Fast and Minimalist, it is called <a href=\"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/abiosoft/colima\">Colima</a>. After simple setup from <a href=\"https://brew.sh\">Home Brew</a> and you are good to go.</p>\n",
"date_published": "2022-07-20T12:50:56+10:00",
"url": "https://micro.benji.ltd/2022/07/20/docker-runtime-without.html"
},
{
"id": "https://benny.micro.blog/2022/07/18/fix-io-freeze.html",
"title": "Fix I/O freeze with Linux have large RAM",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<h2 id=\"tldr\">TLDR;</h2><blockquote>According to this <a href=\"https://github.com/pop-os/default-settings/pull/121\">pull-request</a> set <code>vm.dirty_bytes</code> to <code>268435456</code> and <code>vm.dirty_background_bytes</code> to <code>134217728</code> which is same as 256 Mb and 128 Mb.</blockquote><p>I never had any issue on my Linux laptop before. Well, at least before I install <a href=\"https://steampowered.com\">Steam</a> on my Linux laptop. Linux gaming is really sky-rocketing when the time I bought my gaming laptop. Thanks to Valve and Proton, gaming on Linux is more and more reliable after the <a href=\"https://www.gamingonlinux.com/2022/07/steam-deck-hits-over-4000-titles-marked-either-verified-or-playable/\">Steam Deck release</a>. I can enjoy almost every game in my Steam library. When I start to download games I notice there is some random freeze during the download and game installation.</p><p>After some investigation I realise it's most likely because my BTRFS + CryptSetup bottleneck my NVMe performance. However, at the time when I decide which encode I should setup my BTRFS partition, I already done the research that normal usage of I/O will not cause the freeze based on calculations, it should be totally fine. Hence I towards my direction to dirty page.</p><h2 id=\"dirty-page-how-dirty-is-that\">Dirty page, how dirty is that?</h2><p>About how dirty page works, this <a href=\"https://unix.stackexchange.com/a/579781\">StackOverflow</a> answer will describe this much more detail and better than me.</p><p>I'm not going to explain all the technical details about how dirty page works in Linux, but can picture you this. Dirty page is like <code>pagefile</code> in Windows, but instead of create a <code>pagefile.sys</code> inside your <code>C:\\</code>, or like Linux's <code>swapfile</code> sits inside your partition, it put cache into RAM and till the time free pages are all full, Kernel will generate a flusher process to flush the dirty pages. <code>dirty_ratio</code> should be around 20 percents of your total RAM, and <code>dirty_background_ratio</code> should be 10 percents of your total RAM by default. In my case it is around 4.8 G of <code>dirty_background_ratio</code> and 9.6 G of <code>dirty_ratio</code>. This size is based on 48G RAM sits inside my laptop. There is no such of HDD or NVMe drive can handle 4.8G write speed per second, eventually will make Kernel to hang all the process and wait flusher to clean out the dirty pages.</p><h2 id=\"how-to-prevent-io-freeze\">How to prevent I/O freeze</h2><p>First thought is reduct dirty ratio percentage, but you cannot just lower the value and hope it will fix everything. Zero out both <code>dirty_ratio</code> and <code>dirty_background_ratio</code> will simply make your system not usable because Kernel flusher process is constantly writing data to HDD, making system to freeze almost all the time. So the ideal situation is find the perfect value of <code>dirty_background_ratio</code> and <code>dirty_ratio</code> make flusher can process dirty page without hanging all the process on the side. However, I do not know how to accomplish this due to I have no idea how the calculation works with <code>dirty_ratio</code> even after few days of googling around. Moreover, <code>dirty_ratio</code> and <code>dirty_background_ratio</code> will not accept decimal as value, so the lowest value will be around 480Mb (1%) and 960Mb(2%).</p><h2 id=\"percentage-or-bytes\">Percentage or Bytes?</h2><p>From reading <a href=\"https://www.kernel.org/doc/html/latest/admin-guide/sysctl/vm.html#dirty-background-ratio\">The Linux Kernel documentation</a>, I realise there is an alternative option about <code>dirty_ratio</code> is defined as <code>dirty_bytes</code>. Which means I can limit the dirty page to precise size, which is more reliable in my case. After tried different sizes of <code>dirty_bytes</code> without success, I decide to dig more into various Linux distros. Hopefully I can find one distro has default setting of <code>dirty_bytes</code> but not <code>dirty_ratio</code> instead. </p><h2 id=\"solution-thank-you-random-stranger\">Solution, Thank you random stranger!</h2><p>After awhile I found that Pop!OS Github have a <a href=\"https://github.com/pop-os/default-settings/issues/111\">similar issue</a>. Moreover, there is a pull-request which is using <code>dirty_bytes</code>. I put those values into my <code>/etc/sysctl.conf</code> and system is almost freeze-free! </p><p>However random freeze still happens from time to time when install a game or doing large file transferring, I'm happy for what I get now. If someday I decided to remove <code>cryptsetup</code> from my system and stick to non-encrypt BTRFS (which full wipe is needed), Probably will never get freeze again.</p>\n",
"date_published": "2022-07-18T22:31:54+10:00",
"url": "https://micro.benji.ltd/2022/07/18/fix-io-freeze.html"
},
{
"id": "https://benny.micro.blog/2022/05/26/disable-internal-keyboard.html",
"title": "Disable internal keyboard when external keyboard is connected",
"content_html": "<!-- raw HTML omitted -->\n<h1 id=\"use--xinput-list-to-check-your-keyboard-name\">use $ xinput list to check your keyboard name</h1>\n<p>deviceName=“Asus Keyboard”</p>\n<h1 id=\"awk-is-the-command-to-split-return-the-string-which-is-greped-with-command-grep\">awk is the command to split return the string which is greped with command grep.</h1>\n<h1 id=\"in-print-4--n-is-the-number-of-column-of-the-string\">In ‘{print $4 }’, $n is the number of column of the string.</h1>\n<h1 id=\"you-should-be-test-it-before-put-this-into-command-down-below\">You should be test it before put this into command down below.</h1>\n<p>builtInId=$(xinput list | grep $deviceName | awk ‘{ print $4 }’ | grep -Eo ‘[0-9]{1,2}’ | sort | head -n 1)</p>\n<p>case $1 in\nadd)<br>\nxinput disable $builtInId\n# dbus-launch is needed for send notification.\ndbus-launch notify-send “HHKB Connected…” \\ “Internal keyboard disabled !”;;\nremove)<br>\nxinput enable $builtInId\ndbus-launch notify-send “HHKB Disconnected…” \\ “Internal keyboard enabled !”;;\nesac<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->This script is able to be run by direct execution, however I often found myself to run the command before I put my keyboard on the laptop. I start to search for an automation solution. There are not a single step-by-step tutorial for this kind of situation. After some digging on the internet I decide to put some work with <!-- raw HTML omitted -->udev<!-- raw HTML omitted -->.<!-- raw HTML omitted --><!-- raw HTML omitted -->What is udev?<!-- raw HTML omitted --><!-- raw HTML omitted -->According to well explaination on <!-- raw HTML omitted -->opensource.com<!-- raw HTML omitted -->:<!-- raw HTML omitted --><!-- raw HTML omitted -->Udev is the Linux subsystem that supplies your computer with device events.<!-- raw HTML omitted --><!-- raw HTML omitted -->In another word, we can make a script to run whatever we need it to, whether it’s connected, disconnected or even state chaging. Consider it like a event trigger for external devices.<!-- raw HTML omitted --><!-- raw HTML omitted -->What we should do here is we are going to create a <!-- raw HTML omitted -->rules<!-- raw HTML omitted -->. First, user rules should located at <!-- raw HTML omitted -->/etc/udev/rules.d/<!-- raw HTML omitted -->. Second, file name should formate like <!-- raw HTML omitted -->xx-name.rules<!-- raw HTML omitted -->, xx present as number from <!-- raw HTML omitted -->00<!-- raw HTML omitted --> to <!-- raw HTML omitted -->99<!-- raw HTML omitted -->, <!-- raw HTML omitted -->udev<!-- raw HTML omitted --> loads it’s config with accending order by these two digits.<!-- raw HTML omitted --><!-- raw HTML omitted -->From <!-- raw HTML omitted -->Arch Wiki<!-- raw HTML omitted --> you can see how to make a simple rules. The concept is the rule have to pin-point the device. Normally <!-- raw HTML omitted -->ID_VENDOR_ID<!-- raw HTML omitted --> and <!-- raw HTML omitted -->ID_MODEL_ID<!-- raw HTML omitted --> will do the job, although there are some exceptions, so you should read through whole wiki before complaining the rule doesn’t trigger. <!-- raw HTML omitted --><!-- raw HTML omitted -->The structure of the rule is quite simple:<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted --># When HHKB is connected\nACTION==“add”, SUBSYSTEM==“usb”, ENV{XAUTHORITY}="/home/user/.Xauthority", ENV{DISPLAY}=":0", ATTRS{idVendor}==“0853”, ATTRS{idProduct}==“0100”, RUN+="/home/<username>/bin/hhkb.sh add"</p>\n<h1 id=\"when-hhkb-is-disconnected\">When HHKB is disconnected.</h1>\n<p>ACTION==“remove”, SUBSYSTEM==“usb”, ENV{XAUTHORITY}="/home/user/.Xauthority", ENV{DISPLAY}=":0", ATTRS{idVendor}==“0853”, ATTRS{idProduct}==“0100”, RUN+="/home/<username>/bin/hhkb.sh remove"<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->You can see I’ve add two more <!-- raw HTML omitted -->ENV<!-- raw HTML omitted --> parameters. Both of them are for <!-- raw HTML omitted -->dunst<!-- raw HTML omitted --> to display notification if script is triggered. When <!-- raw HTML omitted -->udev<!-- raw HTML omitted --> trigger a rule, it will run as root in non-Xorg environment, therefore we need to give <!-- raw HTML omitted -->udev<!-- raw HTML omitted --> current user’s Xorg cookie (which is <!-- raw HTML omitted -->.Xauthority<!-- raw HTML omitted --> located inside <!-- raw HTML omitted -->~<!-- raw HTML omitted --> directory), let root access the user environment so it know what <!-- raw HTML omitted -->DISPLAY<!-- raw HTML omitted --> we are going to use (most of the case you can do <!-- raw HTML omitted -->echo $DISPLAY<!-- raw HTML omitted --> to check). You can see <!-- raw HTML omitted -->.Xauthority<!-- raw HTML omitted --> as a login cookie for current user, and <!-- raw HTML omitted -->DISPLAY<!-- raw HTML omitted --> as the monitor desire to display notification.<!-- raw HTML omitted --><!-- raw HTML omitted -->Ubuntu version above 10.10 seems remove <!-- raw HTML omitted -->.Xauthority<!-- raw HTML omitted --> from <!-- raw HTML omitted -->~<!-- raw HTML omitted -->, try <!-- raw HTML omitted -->echo $XAUTHORITY<!-- raw HTML omitted --> to see if you can locate the cookie file.<!-- raw HTML omitted --><!-- raw HTML omitted -->There are some posts online says that you can also add these two environment variable inside your script, however I find myself it is more confortable to let it sit inside <!-- raw HTML omitted -->rules<!-- raw HTML omitted --> files since I’m the only one using this machine.<!-- raw HTML omitted --><!-- raw HTML omitted -->Script is ready, Rules are set, now what?<!-- raw HTML omitted --><!-- raw HTML omitted -->First of all, put your script into <!-- raw HTML omitted -->~/bin<!-- raw HTML omitted --> or any directory inside your <!-- raw HTML omitted -->PATH<!-- raw HTML omitted -->, remember to <!-- raw HTML omitted -->chmod +x hhkb.sh<!-- raw HTML omitted -->, after that <!-- raw HTML omitted -->xx-hhkb-events.rules<!-- raw HTML omitted --> to <!-- raw HTML omitted -->/etc/udev/conf.d/<!-- raw HTML omitted -->. After two files are ready, run <!-- raw HTML omitted -->udevadm control –reload<!-- raw HTML omitted --> as root. Unplug your keyboard and see the magic happens!<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->mv xx-hhkb-events.rules /etc/udev/conf.d/\nmv hhkb.sh ~/bin\nchmod +x ~/bin/hhkb.sh\n<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->It worked! I wanna do some more automation!<!-- raw HTML omitted --><!-- raw HTML omitted -->There are tons of automation can be done with udev, such as battery level warning, temperature warning, even disable your trackpad when your mice / dungle is plugged in. All you need to do is mock the rules file above, and change the <!-- raw HTML omitted -->idVendor<!-- raw HTML omitted --> and <!-- raw HTML omitted -->idProduct<!-- raw HTML omitted --> to your desire device, write the script and you are good to go.<!-- raw HTML omitted --><!-- raw HTML omitted -->You can check all the <!-- raw HTML omitted -->attributes<!-- raw HTML omitted --> by using <!-- raw HTML omitted -->udevadm info –attribute-walk –path=<device><!-- raw HTML omitted -->, and by using <!-- raw HTML omitted -->udevadm monitor –environment<!-- raw HTML omitted -->, unplug and see the environment value.<!-- raw HTML omitted --><!-- raw HTML omitted -->Sidenote<!-- raw HTML omitted --><!-- raw HTML omitted -->My laptop have two “Asus Keyboard” list inside <!-- raw HTML omitted -->xinput list<!-- raw HTML omitted -->, one is for function keys and one is for keyboard. Make sure you disable the right one or both of them, from my case I just disable the keyboard but not function keys is enough for me.<!-- raw HTML omitted --><!-- raw HTML omitted -->Bonus<!-- raw HTML omitted --><!-- raw HTML omitted -->If you are using HHKB and also like to use it’s layout when using built-in keyboard, add <!-- raw HTML omitted -->setxkbmap -option<!-- raw HTML omitted --> (reset mapping) inside <!-- raw HTML omitted -->add<!-- raw HTML omitted --> case and <!-- raw HTML omitted -->setxkbmap -option ctrl:swapcaps,altwin:swap_lalt_lwin<!-- raw HTML omitted --> inside <!-- raw HTML omitted -->remove<!-- raw HTML omitted --> case.<!-- raw HTML omitted --></p>\n",
"content_text": "<figure class=\"kg-card kg-image-card\"><img src=\"uploads/2023/523e654270.jpg\" class=\"kg-image\" alt loading=\"lazy\" width=\"537\" height=\"98\"></figure><h2 id=\"tldr\">TL;DR;</h2><p>Check the <a href=\"https://gist.github.com/p3nj/74a746df417aca243a249be2ad556ead\">Github Gist</a> I made for this post.</p><h2 id=\"you-should-try-put-your-hhkb-on-your-laptop\">You should try put your HHKB on your laptop</h2><p>I really do enjoy putting my HHKB on my laptop while I'm working with it, this habbit follows me when I was using my Macbook pro with <a href=\"https://thenextweb.com/news/what-hell-apple-butterfly-keyboard-keys-design\">butterfly keyboard</a>. This keyboard feels good at first, however, <a href=\"https://www.macrumors.com/guide/butterfly-keyboard-issues/\">lack of ESC and multiple issues</a> made me can not stand it anymore. Some day I found out there's alot of people <a href=\"https://search.brave.com/images?q=HHKB+on+macbookpro&source=web#3\">putting their HHKB on Macbook and it sit flush</a>, moreover, because some magic coincidence design of HHKB, it will sit flush on Macbook pro without touching any keys. Soon I follow this, I have the most satisfying typing experience in my life. This habbits follows me for couple of years.</p><p>Time goes by, my Macbook pro can't catch up my workspace anymore, so I bought a <a href=\"https://rog.asus.com/us/laptops/rog-zephyrus/2021-rog-zephyrus-g14-series/\">Asus G14 2021</a> with top-spec at the time, full on Arch mode. Sadly when I try to put my HHKB on it keyboard doesn't fit on inbuilt keyboard, so if you going to put your HHKB on your laptop like on Macbook pro, you will have to disable built-in keyboard.</p><h2 id=\"let-the-scripting-begins\">Let the scripting begins</h2><p>At first I wrote a simple script manage to disable my built-in keyboard. </p><pre><code class=\"language-bash\">#!/bin/bash\n\n# use $ xinput list to check your keyboard name\ndeviceName=\"Asus Keyboard\"\n# awk is the command to split return the string which is greped with command grep.\n# In '{print $4 }', $n is the number of column of the string.\n# You should be test it before put this into command down below.\nbuiltInId=$(xinput list | grep $deviceName | awk '{ print $4 }' | grep -Eo '[0-9]{1,2}' | sort | head -n 1)\n\ncase $1 in\n add) \n xinput disable $builtInId\n # dbus-launch is needed for send notification.\n dbus-launch notify-send \"HHKB Connected...\" \\ \"Internal keyboard disabled !\";;\n remove) \n xinput enable $builtInId\n dbus-launch notify-send \"HHKB Disconnected...\" \\ \"Internal keyboard enabled !\";;\nesac</code></pre><p>This script is able to be run by direct execution, however I often found myself to run the command before I put my keyboard on the laptop. I start to search for an automation solution. There are not a single step-by-step tutorial for this kind of situation. After some digging on the internet I decide to put some work with <code>udev</code>.</p><h2 id=\"what-is-udev\">What is udev?</h2><p>According to well explaination on <a href=\"https://opensource.com/article/18/11/udev\">opensource.com</a>:</p><blockquote>Udev is the Linux subsystem that supplies your computer with device events.</blockquote><p>In another word, we can make a script to run whatever we need it to, whether it's connected, disconnected or even state chaging. Consider it like a event trigger for external devices.</p><p>What we should do here is we are going to create a <code>rules</code>. First, user rules should located at <code>/etc/udev/rules.d/</code>. Second, file name should formate like <code>xx-name.rules</code>, xx present as number from <code>00</code> to <code>99</code>, <code>udev</code> loads it's config with accending order by these two digits.</p><p>From <a href=\"https://wiki.archlinux.org/title/udev(\">Arch Wiki</a> you can see how to make a simple rules. The concept is the rule have to pin-point the device. Normally <code>ID_VENDOR_ID</code> and <code>ID_MODEL_ID</code> will do the job, although there are some exceptions, so you should read through whole wiki before complaining the rule doesn't trigger. </p><p>The structure of the rule is quite simple:</p><pre><code class=\"language-conf\"># When HHKB is connected\nACTION==\"add\", SUBSYSTEM==\"usb\", ENV{XAUTHORITY}=\"/home/user/.Xauthority\", ENV{DISPLAY}=\":0\", ATTRS{idVendor}==\"0853\", ATTRS{idProduct}==\"0100\", RUN+=\"/home/<username>/bin/hhkb.sh add\"\n# When HHKB is disconnected.\nACTION==\"remove\", SUBSYSTEM==\"usb\", ENV{XAUTHORITY}=\"/home/user/.Xauthority\", ENV{DISPLAY}=\":0\", ATTRS{idVendor}==\"0853\", ATTRS{idProduct}==\"0100\", RUN+=\"/home/<username>/bin/hhkb.sh remove\"</code></pre><p>You can see I've add two more <code>ENV</code> parameters. Both of them are for <code>dunst</code> to display notification if script is triggered. When <code>udev</code> trigger a rule, it will run as root in non-Xorg environment, therefore we need to give <code>udev</code> current user's Xorg cookie (which is <code>.Xauthority</code> located inside <code>~</code> directory), let root access the user environment so it know what <code>DISPLAY</code> we are going to use (most of the case you can do <code>echo $DISPLAY</code> to check). You can see <code>.Xauthority</code> as a login cookie for current user, and <code>DISPLAY</code> as the monitor desire to display notification.</p><blockquote>Ubuntu version above 10.10 seems remove <code>.Xauthority</code> from <code>~</code>, try <code>echo $XAUTHORITY</code> to see if you can locate the cookie file.</blockquote><p>There are some posts online says that you can also add these two environment variable inside your script, however I find myself it is more confortable to let it sit inside <code>rules</code> files since I'm the only one using this machine.</p><h2 id=\"script-is-ready-rules-are-set-now-what\">Script is ready, Rules are set, now what?</h2><p>First of all, put your script into <code>~/bin</code> or any directory inside your <code>PATH</code>, remember to <code>chmod +x hhkb.sh</code>, after that <code>xx-hhkb-events.rules</code> to <code>/etc/udev/conf.d/</code>. After two files are ready, run <code>udevadm control --reload</code> as root. Unplug your keyboard and see the magic happens!</p><pre><code>mv xx-hhkb-events.rules /etc/udev/conf.d/\nmv hhkb.sh ~/bin\nchmod +x ~/bin/hhkb.sh\n</code></pre><h2 id=\"it-worked-i-wanna-do-some-more-automation\">It worked! I wanna do some more automation!</h2><p>There are tons of automation can be done with udev, such as battery level warning, temperature warning, even disable your trackpad when your mice / dungle is plugged in. All you need to do is mock the rules file above, and change the <code>idVendor</code> and <code>idProduct</code> to your desire device, write the script and you are good to go.</p><p>You can check all the <code>attributes</code> by using <code>udevadm info --attribute-walk --path=<device></code>, and by using <code>udevadm monitor --environment</code>, unplug and see the environment value.</p><h2 id=\"sidenote\">Sidenote</h2><p>My laptop have two \"Asus Keyboard\" list inside <code>xinput list</code>, one is for function keys and one is for keyboard. Make sure you disable the right one or both of them, from my case I just disable the keyboard but not function keys is enough for me.</p><h2 id=\"bonus\">Bonus</h2><p>If you are using HHKB and also like to use it's layout when using built-in keyboard, add <code>setxkbmap -option</code> (reset mapping) inside <code>add</code> case and <code>setxkbmap -option ctrl:swapcaps,altwin:swap_lalt_lwin</code> inside <code>remove</code> case.</p>\n",
"date_published": "2022-05-26T18:11:14+10:00",
"url": "https://micro.benji.ltd/2022/05/26/disable-internal-keyboard.html"
},
{
"id": "https://benny.micro.blog/2022/05/14/your-phone-probably.html",
"title": "Your phone probably listening every words you said",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>Sorry at first being some kind of click-bait title but I have not find a proper way to open this post. From the last article I wrote about how DuckDuckGo changed their policy and doing with their algorithm. I promised that I will wrote an article about how phones now days are listening your conversations and turn it into personalized advertisements.</p><h2 id=\"audio-recognization\">Audio Recognization</h2><p>First of all we need to talk about this, audio recognization. Remember when you first setup your smart phone, it politely ask if you want to turn on <em>Audio Recognization</em> of the phone for <strong>better</strong> experience? This method was going to improve your phone to recognize your voice when you using either <strong><em>Ok Google</em></strong> or <strong><em>Hey Siri</em></strong>. Intentions of this function is good and convenience, probably too convenience, it makes your phone <em>constantly</em> listen what you saying and waiting for keywords to trigger the assistance.</p><h2 id=\"the-good-and-the-bad\">The Good and The Bad</h2><p>This audio monitoring or whatever you decide to call it, it’s mostly done by good purpose. Is to make your phone more reachable for you, help you set your timer or schedule your meeting, or even an event on calendar. More you use them, more you get used to it. This is where the evil plan sneaks in.</p><p>I remember after few months after audio assistance can be active by your voice without touching your phone, some websites start pushing out articles about how phone record your conversations and <em>magically</em> some keywords started to pop out as form of advertisements. After few months more people notice this, they decide to change their methods by adding a confirm page, request your agreement to use your audio data. Shortly after, a <a href=\"https://www.businessinsider.com/siri-new-always-on-feature-has-privacy-implications-2015-9\">INSIDER Article</a> reveals the background of this whole operation.</p><h2 id=\"let%E2%80%99s-shoot-out-most-effective-advertisement\">Let’s shoot out most effective advertisement</h2><blockquote>Surveillance is all about convenience - Bruce Schneier</blockquote><p>Umm, I’m not sure about that, if I’m going to let a smartphone listen all my conversation what will be next? You guessed it! Advertisement!</p><p>The final form (well, at least for now) is trying to pinpoint the correct audience, make personalize advertisement more efficient and lower the cost. Nothing is better than pushing out an advertisement about catnip towards people who just had a conversation about their cats. But there must be a way to stop this, right?</p><h2 id=\"how-to-avoid-it\">How to avoid it</h2><p>Most of the articles online now is suggesting you whether turn off your virtual assistance, using a VPN, or keep your software up to date (huh?) to avoid conversations being upload to cloud. Are you sure you want to actually disable Siri or OK google helping you when your both hands are busy? Moreover this VPN thing, it’s not even an option to avoid these kind of things, companies will still identify you by various ways. However, most the phone now days you can decide to share your audio or not at setup page, simply disable that option will stop the personalize advertisement magically pops out. Or will it?</p><h2 id=\"sorry-there-is-no-sweet-spot-here\">Sorry, there is no sweet spot here</h2><p>Between being listen or not being listen, unfortunately sweet spot of this did not exists (yet), you can not have it all. I tried to disable audio sharing option at the setup page but finds out that I am still, getting pushed by personalized advertisement. Because I am still using various app to <em>Voice Call</em> to my friends. Where those app has requested me allow them to use my microphone. Simply disable core function of the phone is only effective on basis usage. When you allow an app to use your microphone, there will always a leak to share your conversation during using that app. Best way to avoid it is only use FaceTime or regular phone calls and not use any of other voice call app.</p><h2 id=\"what-would-you-think-about-this\">What would you think about this?</h2><p>Being listen? Or rather not?</p><p>I am so used to Siri helps me to start a timer while doing laundry, reminds me return a call 10 minutes later. Even schedule my events and etc... It is too convenience and seamless to a point that when I am at some places can not speak freely, I find myself have trouble to find all the options inside calendar and reminders. I am sure they designed this purposely to make users feel it is easier to set things up by their virtual assistance but not by their own hands. And this is how they make you can not turn virtual assistance off. I guess, surveillance actually is all about convenience.</p>\n",
"date_published": "2022-05-14T02:12:15+10:00",
"url": "https://micro.benji.ltd/2022/05/14/your-phone-probably.html"
},
{
"id": "https://benny.micro.blog/2022/04/28/ssl-handshake-failed.html",
"title": "SSL Handshake failed on ASP .NET Core Linux Container",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>When you try to send a request from Linux container via <code>HttpClient</code>, depends on target <code>URI</code>, request will fail and throw an exception with <code>SSL Handshake with OpenSSL error - SSL_ERROR_SSL</code>.</p><p>When encounter this problem first thought is because <code>HttpClient</code> failed to validate SSL certificate. However after reading through all Inner Exception my eyes drops on this line:</p><p><code>routines:tls_process_ske_dhe:dh key too small</code></p><p>Bingo. Looks like the <code>openssl.cnf</code> inside my container on K8s have more strict setting than the website's SSL certificate, gladly this is an easy fix.</p><h2 id=\"solution\">Solution</h2><p>Inside our <code>Dockerfile</code> we have to add two lines of <code>RUN</code> command in order to modify our container's <code>openssl.cnf</code> settings. </p><pre><code class=\"language-Dockerfile\">## .... \nRUN sed -i 's/DEFAULT@SECLEVEL=2/DEFAULT@SECLEVEL=1/g' /etc/ssl/openssl.cnf\nRUN sed -i 's/DEFAULT@SECLEVEL=2/DEFAULT@SECLEVEL=1/g' /usr/lib/ssl/openssl.cnf \n## ....</code></pre><p>What these two line does is change the value of <code>SECLEVEL</code> from 2 to 1 in both of the <code>openssl.cnf</code> file. After append these two lines, recompile the image and everything will work as expected.</p>\n",
"date_published": "2022-04-28T15:51:46+10:00",
"url": "https://micro.benji.ltd/2022/04/28/ssl-handshake-failed.html"
},
{
"id": "https://benny.micro.blog/2022/04/17/search-engine-and.html",
"title": "Search engine and censorship",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<blockquote>Every day millions of people rely on our free all-in-one privacy solution. Our mobile browser and desktop extension come with private search and seamless protection from trackers as you browse. We deliver privacy, simplified.</blockquote><p>Looks familiar? This is the quote from DuckDuckGo. The first paragraph you see when you enter <a href=\"https://duckduckgo.com/about\">DuckDuckGo about page</a>, but, when you go on <a href=\"https://archive.org/web/\">Wayback Machine</a> and search this about page. You will find something interesting.</p><h2 id=\"the-promise-has-changed\">The Promise has changed</h2><p>On Wayback machine, DuckDuckGo’s about page on <a href=\"https://web.archive.org/web/20130108194836/https://duckduckgo.com/about\">January 8, 2013</a>, you will find two key feature they offer if you decide to use their service.</p><ol><li>No Trackers</li><li>No <strong>filter bubble</strong></li></ol><p>These two key feature is the main seller, but one thing you can’t find it on today’s DuckDuckGo’s about page. Yes, <strong>they do filter now</strong>.</p><h2 id=\"so-what-those-features-do\">So... What those features do?</h2><p>If you are normal, not geeky like me. You probably will not understand today’s article is about. Let me walk you through it.</p><h3 id=\"have-this-ever-happens-to-you\">Have this ever happens to you?</h3><p>One day you were searching some cat nips for your lovely and evil cat. Boom, there is an advertisement pops out after couple of clicks and show you various brands of cat nips you can buy, some of it even have some amazing coupons. That is what trackers does. They track everything you search, by some algorithm they did to your search information and push the advertisement to you.</p><p>They analyze your data and decide which advertisement you should receive, or track your behavior of various search results. That is how search engine service make their benjamins <em>(get it? 😉)</em>.</p><p>In my company, we put trackers on every links to our ECommerce site. Our marketing team will analyze user behavior to improve user experience, or use the result to decide next target audience. Sounds acceptable right?</p><p>Today, people are more focusing about their digital privacy, tolerance of being track has reach the new low. Simply because people started to think they are being track when on the internet, random advertisement appears after a oral conversation or maybe some inappropriate advertisement shows up at important meetings based on their search results last night. <em>(yes, your phone is listening to everything you said, I will write an article about it later)</em></p><p>People decide to lose those trackers by turning on the browser settings named “Send NoTrack To Websites”, but guess what? This is not that effective at all. It can be bypassed by various ways.</p><h3 id=\"how-about-filters\">How about filters?</h3><p>Remember the first time you enter porn site? You have to lie about your age. That is what filter basically does, but you do not have the control of whether enter the site by lying your age or not.</p><p>There is <strong>a lot</strong> of politics incorrect websites on the internet (it is kind of fun to visit, though). It can be search by some very simple keywords, but company will not let this happen since it have chance turn into bad influence to public reputations for their investor. What do they do? They filter these kinds of website out of their search result as much as they can.</p><p>According to DuckDuckGo’s filter bubble is kind of working with trackers, but in more advance way. Think of this, when we both search for bluetooth speakers, your first result is eBay, but I get Amazon, it is based on personal preference which we choose to go the most. DuckDuckGo <strong>promise</strong> there is no filter bubble on their search results. This was the most greatest concept of internet equity of the time.</p><p>At first I didn’t use DuckDuckGo as much I use Google, because back in around 2015, search results of DuckDuckGo is filled with spam and porn site. I often find myself digging through results by filter those websites. I know there have some progress they have to figure it out, so I still coming back time to time. Results are too, getting better and better. Around 2018, I fully switched to DuckDuckGo, and one day I see this <a href=\"https://www.reddit.com/r/privacy/comments/di4xd6/duckduckgo_is_censoring_results/\">reddit post</a> from r/privacy on reddit.</p><h2 id=\"censor-and-remove-results\">Censor and Remove results</h2><p>At first it did not bothers me, I honestly do not care about random porn site is being censored, but I put DuckDuckGo to probation. For awhile it does not seems growing, so I did not pay much attention about how DuckDuckGo threats their results. Last month a news comes out titled as <a href=\"https://www.engadget.com/duck-duck-go-reverses-course-will-demote-russian-propaganda-in-search-results-014336389.html\"> DuckDuckGo will demote Russian propaganda in search results </a>. Okay, this is understandable for me, based on the situation of Ukraine with Russia, this kind of adjustment is reasonable enough to keep public not effective by the... <em>Hold on, this is filtering right?</em></p><p>If you stand your point say something like “It is not filtering, it just order the result from relative to relative + country!”. Alright, I am going to let this one slide then, but next one is not bearable at all.</p><h2 id=\"duckduckgo-remove-results-for-major-pirate-site\">DuckDuckGo remove results for major pirate site</h2><p><a href=\"https://www.engadget.com/duckduckgo-removes-pirate-sites-204936242.html\">DuckDuckGo removes search results for major pirate websites</a></p><p>Honestly I was shock when I read this title. I dash to their about page because I <strong>knew</strong> they promised they will not do such thing. This is why I use their service at the first place, and voila, promise is gone.</p><p>At some point, DuckDuckGo give up being uncensored, and not filtered search engine to user privacy focused website. I felt betrayed, after all these years being so loyalty, recommend their service every time I had a chance, this is what they ended up to. A search engine should threat all results equally, no censorship, no filter and everyone should get the same results by their algorithm.</p><h2 id=\"alternative\">Alternative?</h2><p>I do some research after I saw this news, looks like <a href=\"https://brave.com/search/\">Brave</a> launches their own search engine recently. There is some really interesting selling point such as Independent search and Search without a trace. About censorship I’ve seen a <a href=\"https://community.brave.com/t/braves-official-stance-on-censorship/194451\">post</a> on their forum asking but there have not had an official reply for it.</p><p>I will give <a href=\"https://search.brave.com\">Brave</a> a shot and see where it goes.</p><blockquote>The fact is that censorship always defeats its own purpose, for it creates, in the end, the kind of society that is incapable of exercising real discretion, incapable, that is, of doing an honest or intelligent job, and thus guarantees a steady intellectual decline... - Henry Steele Commager</blockquote>\n",
"date_published": "2022-04-17T02:22:18+10:00",
"url": "https://micro.benji.ltd/2022/04/17/search-engine-and.html"
},
{
"id": "https://benny.micro.blog/2022/04/16/some-thoughts-about.html",
"title": "Some thoughts about NFT",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>If you follows all kinds of technology news every day like me, you must have notice this NFT is bursting everywhere. I believe even at this time there is a lot of people who don't understand what NFT is and why everyone on the Internet is throwing money into it.</p><p>Basically, you can think NFT is like a digital identity of an object. Currently, most of the object put on NFT is art, painting, and weird avatar pictures. From my perspective NFT is toward the correct way of internet evolving, but there is some major problem with it.</p><h2 id=\"technology-is-not-there-yet\">Technology is not there yet</h2><p>From my point if view, the current state of NFT is not in the production situation yet. If you are already pissed off by seeing this just bear with me. Let me explain.</p><p>If you buy an art online or at the auction, got someone to deliver to your door, you can not ever trust anyone, but the professional tells you the art you bought is legit. With NFT you <strong>sure</strong> your purchases are automatically verified as the only authentic object in the world, but why?</p><h2 id=\"non-fungible-token-or-nft-for-short\">Non-Fungible Token or NFT for short</h2><p>Long story short, besides all the tech details, I can explain this fairly quickly. Back in your childhood, you must have collected some baseball cards, Pokémon cards or basketball cards. You trade your cards to other kids to get your favorite cards. It is just like that. You can see this as trading cards, but here is the catch, each card have one and only copy that is legit, rest of it just like random china made low quality cards. If you purchase a Non-Fungible Token or NFT, you are the only one who owns the legit copy of the item.</p><p><em>It is basically like card trading on steroids.</em></p><h2 id=\"the-actually-catch\">The Actually Catch</h2><p>Just like how card trade ends, the game ends when the hype is over. If no one is interested with NFT anymore, you are stuck with it forever. Well, if you really like the NFT you bought, good for you, but for most of the collectors in the market right now, they <strong>need</strong> the hype to keep this game rolling. So they can continuously flip the price over and over again. Did not this sound familiar? You are right.</p><blockquote>A Musical Chair game, but last one lose.</blockquote><h2 id=\"should-you-stay-away-for-now\">Should you stay away for now?</h2><p>I’ll say it depends, if you see something really catches your eyes and the price is right, I will say go get it, support the author. As this point, the technology is not there for more than just a trading game, but there is <strong>a lot</strong> of potential with this. I’m waiting for NFT can do more than just a decoration for your digital room or some item in the digital game or social media avatar, but actually have some connection to the real world.</p><h2 id=\"real-world-scenario\">Real World Scenario</h2><p>I’m willing to see in the near future, every object binds with NFT. When you purchase a car, only you can start the engine of the car since you’re the only one have NFT binds with the car, or enter an apartment binds with a NFT you own. People can share their account <em>(or wallet address)</em> to trusted users for access the object. That is when I recommend to dive in.</p><p>For now, NFT is digital art market, non of the items you bought is connecting to the real world. You can try to play it a bit, but if you trying to flip your money with it, just remember to be careful.</p>\n",
"date_published": "2022-04-16T04:18:23+10:00",
"url": "https://micro.benji.ltd/2022/04/16/some-thoughts-about.html"
},
{
"id": "https://benny.micro.blog/2022/04/13/export-azure-keyvault.html",
"title": "Export Azure KeyVault Certificate with Private Key",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p><a href=\"__GHOST_URL__/extract-certificate-from-azure-key-vault/\">Last post</a> we manage to extract certificate from Azure KeyVault. Thing is, using such method will only retrieve Public Key of certificate. It is not possible to retrieve Private Key from the method previous used.</p><!--kg-card-begin: markdown--><p>Since we are aimimg Private Key, we need to change the way we retrieve certificate. From <code>GetCertificateAsync</code> to <code>DownloadCertificateAsync</code>, we can actually <strong>download</strong> certificate into our memory.</p>\n<pre><code>//var certificate = await client.GetCertificeAsync(certName);\nvar certificate = await client.DownloadCertificateAsync(certName);\n</code></pre>\n<h2 id=\"but-it-does-not-work\">But it does not work</h2>\n<p>Tricky part is, you can not extract Private Key from that.<br>\nEven when it shows <code>HasPrivateKey = true</code>.</p>\n<p>When encounter this problem, the scenario is trying to convert the Private Key to <code>AsymmetricKeyParameter</code>. It supposed to sign my request to the APIs. Which need to do something like this:</p>\n<pre><code>Stream stream = new MemoryStream(certificate.Value.Export(X509ContentType.Pkcs12 , certPwd.ToCharArray()));\nPkcs12Store store = new(stream, certPwd.ToCharArray());\nstring pName = default;\nforeach (string n in store.Aliases)\n{\n if (store.IsKeyEntry(n))\n {\n pName = n;\n Console.WriteLine(n);\n }\n}\nConsole.WriteLine(store.GetKey(pName).Key);\n</code></pre>\n<p>As you can see, it needs to store the certificate into <code>Pkcs12Store</code> which can only be created by using <code>Stream</code>. when <code>X509ContentType</code> is not defined when download the certificate, it will not allow you to do such thing. To solve this we need to add <code>X509ContentType.Exportable</code> into certificate's <code>X509StorageFlags</code>.</p>\n<p>First I tried to add <code>X509StorageFlags</code> to <code>X509Certificate2</code> <strong>After</strong> it's being downloaded, sadly it will not work this way.</p>\n<h2 id=\"the-solution\">The solution</h2>\n<p>After digging awhile I figure out <code>DownloadCertificateAsync</code> comes with an option called <code>DownloadCertificateOptions</code>. Which you can define <code>X509StorageFlags</code> before download the certificate. After a little bit modification, my code become like this:</p>\n<pre><code>DownloadCertificateOptions downloadOption = new(certificateName)\n{\n KeyStorageFlags = X509KeyStorageFlags.MachineKeySet |\n X509KeyStorageFlags.Exportable\n};\nvar certificate = await client.DownloadCertificateAsync(downloadOption);\n</code></pre>\n<p>With this modification, certificate can be exported with <code>Pkcs12</code> format and convert it to <code>AsymmetricKeyParameter</code> without any problem.</p>\n<!--kg-card-end: markdown-->\n",
"date_published": "2022-04-13T14:53:02+10:00",
"url": "https://micro.benji.ltd/2022/04/13/export-azure-keyvault.html"
},
{
"id": "https://benny.micro.blog/2022/04/11/azure-service-principle.html",
"title": "Azure Service Principle with Azure Identity",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p><a href=\"__GHOST_URL__/extract-certificate-from-azure-key-vault/\">Last post</a> we talked about how to use C# to extract Azure Key Vault credentials. Further more it's ideal to run without problem on localhost, but when you deploy it to the cloud it will throw an exception complaining about authorization with Azure, and here is why.</p><!--kg-card-begin: markdown--><p>At local environment, application will find and use the Azure credentials if you use <code>DefaultAzureCredential</code> method. Inside fresh-everytime docker environment there is no way it can retrieve those credentials. We have to do some extra work for it.</p>\n<!--kg-card-end: markdown--><h2 id=\"create-azure-roll-based-access-control\">Create Azure Roll-Based Access Control</h2><p>This blog post we are going to talk about how to create Azure Roll-based access control, as known as <strong>rbac </strong>client, and put it into work.</p><p>First of all you will have to install <strong>Azure Command-line Interface</strong> in your machine, if you have not done this before you can refer this <a href=\"https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?msclkid=a2363248b97111ecb475f269c010f386\">official documentation</a> by Microsoft. After everything is setup correctly, follow this <a href=\"https://docs.microsoft.com/en-us/cli/azure/create-an-azure-service-principal-azure-cli\">tutorial </a>and create your very first <strong>rbac </strong>client.</p><p>In case you forgot, you should definitely keep those credential somewhere safe. For me, I just simply put it into MSSQL database.</p><!--kg-card-begin: markdown--><p>The credential should looks like this when created:</p>\n<pre><code class=\"language-json\">{\n "appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",\n "displayName": "KeyVaultUser",\n "password": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",\n "tenant": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"\n}\n</code></pre>\n<p>After you store it somewhere safe, inside your program you should replace <code>DefaultAzureCredential()</code> method with <code>ClientSecretCredential(tenantId, clientId, clientSecret);</code></p>\n<!--kg-card-end: markdown--><h2 id=\"setup-rbac-permission-on-azure\">Setup RBAC permission on Azure</h2><p>Before we modify our code, we should head to Azure Portal, find your desire Key Vault to use, and give the client you just created some permissions. </p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"uploads/2023/ca5a9f802e.jpg\" class=\"kg-image\" alt loading=\"lazy\" width=\"915\" height=\"499\" srcset=\"__GHOST_URL__/content/images/size/w600/2022/04/image-2.png 600w, https://benny.micro.blog/uploads/2023/ca5a9f802e.jpg 915w\" sizes=\"(min-width: 720px) 720px\"><figcaption>Azure Portal Key Vault Add Access Policy</figcaption></figure><p>On the picture above you can see. After we entered Key Vault, click the \"<strong>Access Policies</strong>\" option on the left, and click \"<strong>+ Add Access Policy</strong>\" on the right. </p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"uploads/2023/f0921f1146.jpg\" class=\"kg-image\" alt loading=\"lazy\" width=\"730\" height=\"432\" srcset=\"__GHOST_URL__/content/images/size/w600/2022/04/image-3.png 600w, https://benny.micro.blog/uploads/2023/f0921f1146.jpg 730w\" sizes=\"(min-width: 720px) 720px\"><figcaption>Add access policy</figcaption></figure><p>As you can see above. Since we only need our client to retrieve Key, Secret and Certificates, We only give the <strong>rbac </strong>client \"<strong>Get</strong>\"<strong> </strong>permission, and it is consider a best practice.</p><blockquote>Never give extra permission unless you know what you are doing.</blockquote><p>After setting it up, we can start modify our code!</p><h2 id=\"code-modification\">Code Modification</h2><!--kg-card-begin: markdown--><p>When preparation above is finished, we are going to change how program retrieve credentials. By doing so, we are going to use <code>ClientSecretCredential</code> method to setup our credential.</p>\n<p>This is just a POC console application so I hard-coded credential when create this object. Ideally you should retrieve this information somewhere safe, either from Environment Variable or Database, pick your favorite.</p>\n<!--kg-card-end: markdown--><figure class=\"kg-card kg-image-card\"><img src=\"uploads/2023/6b905f1c9c.jpg\" class=\"kg-image\" alt loading=\"lazy\" width=\"630\" height=\"104\" srcset=\"__GHOST_URL__/content/images/size/w600/2022/04/image-1.png 600w, https://benny.micro.blog/uploads/2023/6b905f1c9c.jpg 630w\"></figure><p>After finished your modification, run the console application or whatever method you choose to, you should see it retrieve credentials from Azure Key Vault successfully.</p><figure class=\"kg-card kg-image-card\"><img src=\"uploads/2023/117e86760d.jpg\" class=\"kg-image\" alt loading=\"lazy\" width=\"353\" height=\"241\"></figure>\n",
"date_published": "2022-04-11T18:59:38+10:00",
"url": "https://micro.benji.ltd/2022/04/11/azure-service-principle.html"
},
{
"id": "https://benny.micro.blog/2022/04/11/extract-certificate-from.html",
"title": "Extract Certificate from Azure KeyVault",
"content_html": "<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<p>namespace Commandline\n{\nclass Program\n{\nstatic async Task Main(string[] args)\n{\nconst string certificateName = "CertificateName";\nvar keyVaultName = Environment.GetEnvironmentVariable("KEY_VAULT_NAME");\nvar kvUri = $"https://{keyVaultName}.vault.azure.net";</p>\n<pre><code> CertificateClient client = new(new Uri(kvUri), new DefaultAzureCredential());\n\n Console.Write($&quot;Creating a certificate in {keyVaultName} called '{certificateName}' ...&quot;);\n CertificateOperation operation = await client.StartCreateCertificateAsync(certificateName, CertificatePolicy.Default);\n await operation.WaitForCompletionAsync();\n Console.WriteLine(&quot; done.&quot;);\n\n Console.WriteLine($&quot;Retrieving your certificate from {keyVaultName}.&quot;);\n var certificate = await client.GetCertificateAsync(certificateName);\n Console.WriteLine($&quot;Your certificate version is '{certificate.Value.Properties.Version}'.&quot;);\n\n Console.Write($&quot;Deleting your certificate from {keyVaultName} ...&quot;);\n DeleteCertificateOperation deleteOperation = await client.StartDeleteCertificateAsync(certificateName);\n // You only need to wait for completion if you want to purge or recover the certificate.\n await deleteOperation.WaitForCompletionAsync();\n Console.WriteLine(&quot; done.&quot;);\n\n Console.Write($&quot;Purging your certificate from {keyVaultName} ...&quot;);\n await client.PurgeDeletedCertificateAsync(certificateName);\n Console.WriteLine(&quot; done.&quot;);\n }\n}\n</code></pre>\n<p>}\n<!-- raw HTML omitted --><!-- raw HTML omitted --></p>\n<!-- raw HTML omitted -->\n",
"content_text": "<!--kg-card-begin: markdown--><p>Azure KeyVault is a really good place for those who host their applications or kubernetes pool on Azure. It can saftly guard your Keys, Secrets and Certificates.</p>\n<p>We are moving our payment service to kubernetes host lately. Which means we need to ditch the AppService and no local storage for certificates.</p>\n<h2 id=\"install-nuget-packages\">Install Nuget Packages.</h2>\n<p>First, we need to set up our environment for communicate with Azure KeyVault.<br>\nIn desire project, install <code>Azure.Security.KeyVault.Certificates</code> and <code>Azure.Identity</code> nuget package.</p>\n<h2 id=\"modify-current-method\">Modify Current Method</h2>\n<p>Originally we use <code>FilePath</code> to retrieve our certificates. Since we are going to use Azure KeyVault as our storage, we need to modify that part and retrieve our certificate from there.<br>\nGladly Microsoft documentation have detailed instructions.</p>\n<pre><code class=\"language-csharp\">using System;\nusing System.Threading.Tasks;\nusing Azure.Identity;\nusing Azure.Security.KeyVault.Certificates;\n\nnamespace Commandline\n{\n class Program\n {\n static async Task Main(string[] args)\n {\n const string certificateName = "CertificateName";\n var keyVaultName = Environment.GetEnvironmentVariable("KEY_VAULT_NAME");\n var kvUri = $"https://{keyVaultName}.vault.azure.net";\n\n CertificateClient client = new(new Uri(kvUri), new DefaultAzureCredential());\n\n Console.Write($"Creating a certificate in {keyVaultName} called '{certificateName}' ...");\n CertificateOperation operation = await client.StartCreateCertificateAsync(certificateName, CertificatePolicy.Default);\n await operation.WaitForCompletionAsync();\n Console.WriteLine(" done.");\n\n Console.WriteLine($"Retrieving your certificate from {keyVaultName}.");\n var certificate = await client.GetCertificateAsync(certificateName);\n Console.WriteLine($"Your certificate version is '{certificate.Value.Properties.Version}'.");\n\n Console.Write($"Deleting your certificate from {keyVaultName} ...");\n DeleteCertificateOperation deleteOperation = await client.StartDeleteCertificateAsync(certificateName);\n // You only need to wait for completion if you want to purge or recover the certificate.\n await deleteOperation.WaitForCompletionAsync();\n Console.WriteLine(" done.");\n\n Console.Write($"Purging your certificate from {keyVaultName} ...");\n await client.PurgeDeletedCertificateAsync(certificateName);\n Console.WriteLine(" done.");\n }\n }\n}\n</code></pre>\n<p>As you can see above we use Environment Variable to store our KeyVault name, you can also hard-coded it or store it inside your SQL Database.</p>\n<h2 id=\"convert-certificate-to-x509certificate2\">Convert Certificate to X509Certificate2</h2>\n<p>In order to verify and sign our order before request is sent. It is needed that to convert certificate from <code>KeyVaultCertificateWithPolicy</code> to <code>X509Certificate2</code>.</p>\n<p>With a magic single line of code can achieve this.</p>\n<pre><code class=\"language-csharp\">X509Certificate2 certificateWithPrivateKey = new (certificate.Value.Cer, <<CERT PASSWORD>>, X509KeyStorageFlags.MachineKeySet);\n</code></pre>\n<h2 id=\"bonus-convert-it-to-rsacryptoserviceprovider\">Bonus: Convert it to RSACryptoServiceProvider</h2>\n<p>If your method requires different type to work, do the convert like this:</p>\n<pre><code class=\"language-csharp\">certificateWithPrivateKey.GetRSAPublicKey() as RSACryptoServiceProvider;\n</code></pre>\n<p>Hope this simple tutorial helps!</p>\n<!--kg-card-end: markdown-->\n",
"date_published": "2022-04-11T14:43:40+10:00",
"url": "https://micro.benji.ltd/2022/04/11/extract-certificate-from.html"
},
{
"id": "https://benny.micro.blog/2022/04/10/the-newsletter-ive.html",
"title": "The newsletter I’ve been looking for.",
"content_html": "<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "<!--kg-card-begin: markdown--><p>I've been reading news differently from normal person, for years I've been using <a href=\"https://reddit.com\">Reddit</a> and <a href=\"https://9gag.com\">9GAG</a> for latest information, use controversial memes to puzzle the news. I often get distract, it's difficult to scroll through same series of memes at one time. Thus, I found myself end up scrolling random memes and forgot what I was looking for. Hense, I try to build my news reading habits more efficiently. Tried couple of RSS readers or even self-host it, but it seems too hard for me to check readers at the end of the day.</p>\n<h2 id=\"atomic-habits-come-to-rescue\">Atomic Habits come to rescue</h2>\n<p>Couple of days ago I got recommanded to read this book Atomic Habits, I look this book up on my Kobo reader and bought it. This book shows how to make a habit irresistible by combining your normal tasks with some habit you want to build. Such as you want to start a jogging habits, but you find yourself hard to do it. You can either jogging when you're walking your dog, or go for a run in the morning right before you pick up your mail. I do check my work email daily basis, how about put these two together? If I'm able to build a habit to read the newsletters after I checked my work email, I won't find myself scrolling random memes at the end of the day.</p>\n<p>The Idea of combining different tasks to achieve build a new habit is the idea of this post title.</p>\n<h2 id=\"emails-and-news-hmm\">Emails and News, hmm.</h2>\n<p>Back in the day we used to subscribe to various newspapers / magazines and receive latest news everyday morning from your porch. Then the Internet kicks in hard. Newspapers and magazines are turn into digital form, people tend to check on their favorite websites for news. It become massively chaos lately because fake news are plaguing the news enviornment for traffic and advertisement. If you want to verify the article you see is legit you will have to go on various big media websites and trying to dig out the similar title from their lack of UI/UX websites. Some of them does not have a search box for it.</p>\n<p>Thus an idea of reading news from email is pop in my head. Don't get me wrong, email subscription exists from the day internet is being form. Sadly it's too easy to retrieve email list from random people and just spam the useless information to someone's mailbox. All the paradox / scam / controversal letters piling up email service storage. Let's face it, the shape of email ecosystem is bad.</p>\n<h2 id=\"lets-do-it-with-new-mailbox\">Let's do it with new mailbox.</h2>\n<p>It is HARD to clean your personal mailbox. Most of websites won't ask you if you want to subscribe their eDM when you registering, some of them even sends you email regardlessly even they <strong>promise they won't spam</strong>. All kinds of email from Amazon, Twitter and Facebook and even some random websites you never visits are already occupied your mailbox. It's too hard to unsubscribe each one of them and delete all those non-important email, while carefully avoid the email that you want to preserve at the same time.</p>\n<p>Luckly register a new email account is fairaly easy, so I use my iCloud email alternative mailbox just for reciving news letters from various websites I tend to read. After awhile of enjoyment, disaster crawls back.</p>\n<p>My newly created news only mailbox is filling up with random emails again! Somehow email junkies are able to retrieve my email from some website and start spaming my news mailbox and the source is spreading. Till the time I found out, my mailbox is already subscribe dozens of website. I have no clue that which website is giving my email out.</p>\n<p>So I deleted my news mailbox, created another mailbox and subscribe only the majour media company, same thing happens again. The lack of trust of those media company already up to the point that I can't stand it anymore. Eventually I abandoned this idea for awhile.</p>\n<h2 id=\"hop-on-morning-brew\">Hop on Morning Brew</h2>\n<p>It's a Wednesday afternoon, I was browsing random videos on YouTube and this dude have sponsership from a company called <a href=\"https://www.morningbrew.com\">Morning Brew</a>. Suspose this company combine daily digest and latest news, fits it inside an email. I look it up a bit and found out this company is already exists for 25+ years. I went to their website and read some of their latest articles, it's really good that they shrink the most content out but still preserve the correct information, most importantly, <strong>no click bait</strong>.<br>\nShortly after I subscribe their email news letters and I never look back.</p>\n<p>MorningBrew did a great job by collecting all the information, and shink the content to not too long that you have to sit down and read, but fits for taking a bus or subway to work, or waiting in line for your morning coffee at your coffee shop.</p>\n<p>After a few days of trying their service I found out they have more subject focued categories. I also subscribe their <a href=\"https://www.morningbrew.com/sidekick\">Sidekick</a> for life management and productivity hacks, <a href=\"https://www.emergingtechbrew.com\">Emerging Tech Brew</a> for most latest tech newsletters, and <a href=\"https://www.morningbrew.com/money-scoop\">Money Scoop</a> for personal finance newsletters. Only Morning Brew comes into your mailbox daily, Sidekick, Money Scoop and Emerging Tech Brew are twice-weekly. All Morning Brew's emails are in similar layout, easy to read and have links to the topic that you want to dig in more about. The overall reading experience are pleasant.</p>\n<p>I'd like to recommand to everyone who want to save time from browsing all types of media websites. If you find yourself lost in different topic as you tended to, and find yourself diffcult to find the legit information, give Morning Brew a shot, I bet you won't regret it. And they truly won't spam your mailbox.</p>\n<h2 id=\"referral-links\">Referral Links</h2>\n<p>Here is the referral links from morning brew. By clicking this link it won't do anything else but records how many people I introduce to their services, as some rewards I could get some cute stickers!<br>\nYou can find your referral links inside your Morning Brew emails and spread it to your families and friends too.<br>\nIt is much appreciated if you decide to use my links for the first time you hop in :)</p>\n<p>For daily news digest:<br>\n<a href=\"https://www.morningbrew.com/daily/r/?kid=3448b2f4\">Morning Brew</a><br>\nFor life management:<br>\n<a href=\"https://www.morningbrew.com/sidekick/r?kid=3448b2f4\">Side Kick</a><br>\nFor finance:<br>\n<a href=\"https://www.morningbrew.com/money-scoop/r?kid=3448b2f4\">Money Scoop</a><br>\nFor tech:<br>\n<a href=\"https://www.emergingtechbrew.com/r?kid=3448b2f4\">Emerging Tech Brew</a></p>\n<!--kg-card-end: markdown-->\n",
"date_published": "2022-04-10T15:00:09+10:00",
"url": "https://micro.benji.ltd/2022/04/10/the-newsletter-ive.html"
},
{
"id": "https://benny.micro.blog/2022/04/10/regular-update.html",
"title": "Regular Update #01",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>Well, I decide to force myself to blog more, trying to taking notes of my life and saving some memories I might forget, which I find myself hard to remember some details of the story, mind aswell just start it today.</p><hr><p>I got accept by some University, somewhere in Australia, me and Janette are excited since we are seperated for couple of months now.</p><p>While she is working on her Major, I just passed my thirty-bar, and start to think, I should get my Major too, targeting Australia then me and Janette can live back together.</p><p>So I've been working with some documentations and living in double life, working by the day and prepare all the materials I needed at night. Gadly I picked the right agent who handles complicated things, so till this date I got offer by 3/5 University and I believe it's mostly becuase my agent is doing a good job, although it's my second agent, there's some back story into this, probably will write something about it later maybe.</p><hr><p>So I resign my job after some discussion with Janette and funny thing is, after I resign my job my company opens TWO different positions for hiring, till then I realize that I'm always doing two positions job with one position salary. And I'm preparing my IELTS now, I only done some practice on the website and mostly on reading section and listen sections, hope I won't fuck it up.</p>\n",
"date_published": "2022-04-10T02:36:02+10:00",
"url": "https://micro.benji.ltd/2022/04/10/regular-update.html"
},
{
"id": "https://benny.micro.blog/2022/04/05/moved-to-ghostpro.html",
"title": "Moved to Ghost(Pro) and why",
"content_html": "<!-- raw HTML omitted -->\n<p>And I will give you all the information and why I finally done this transfer.<!-- raw HTML omitted --></p>\n<!-- raw HTML omitted -->\n",
"content_text": "<!--kg-card-begin: markdown--><p>Recently I moved my blog from dokku self-host to Offical Ghost(Pro).<br>\nAnd I will give you all the information and why I finally done this transfer.</p>\n<h2 id=\"dont-get-me-wrong-self-host-is-awesome\">Don't get me wrong, self-host is AWESOME.</h2>\n<p>I self-host everything, in my daily routine I use various of services brige me to the WWW like RSS reader, E-Mail, IRC, Social network... and such.</p>\n<p>All these services are managed by RanchOS on multiple droplet.</p>\n<p>Days are good until I resign my job.</p>\n<h2 id=\"moving-out\">Moving out</h2>\n<p>I decide to ditch RanchOS to Kubernetes, first I need to move my blog out to avoid some data loss. so I decide to try Ghost(Pro).</p>\n<p>Few years back I been on Ghost(Pro) for couple of times, it's really nice that user do not have to put hands on the dirty work, it feels like you have Alfred handling all kinds of stuff, but back then cheapest price on Ghost was 15 bucks a month, it's a little bit too much for a Software Engineer which can write a single blog within afternoon or use GitHub Pages, can easily use a 5 bucks VPS to host more than just a blog.</p>\n<p>After single click I can export all my blog post, and upload it to my new Ghost(Pro) blog, so here it is.</p>\n<h2 id=\"moving-back-to-self-host\">Moving back to self-host?</h2>\n<p>I haven't decide if I'm willing to put this blog back to Kubernetes, since I know I'm going to do so many shits to it and probably will face couple of hard restart of this side-project.</p>\n<p>In such case, I think I'm going to stick my blog to Ghost(Pro) for couple of month and wait my K8s is stable enough to move it back.</p>\n<p>And that's it!</p>\n<!--kg-card-end: markdown-->\n",
"date_published": "2022-04-05T12:07:03+10:00",
"url": "https://micro.benji.ltd/2022/04/05/moved-to-ghostpro.html"
},
{
"id": "https://benny.micro.blog/2021/12/24/aks-with-kubernetes.html",
"title": "AKS with Kubernetes Cheat-sheet",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>My work planned to move all our services and website to Kubernetes for better availability, after digging Kubernetes for couple of days I've organized some basic commands.</p><!--kg-card-begin: markdown--><p>First of all, there is no GUI interface of Kubernetes with AKS while I'm writing this, so all the commands requires you have atleast <em>basic</em> CLI knowledge.</p>\n<h2 id=\"login-azure-acr-cli\">Login Azure ACR CLI</h2>\n<p><code>$ az acr login -n <azure container registry name></code></p>\n<h2 id=\"use-az-aks-cli-to-install-kubectl\">Use AZ AKS CLI To Install kubectl</h2>\n<p><code>$ az aks install-cli</code></p>\n<h2 id=\"connect-aks-cli-with-kubectl\">Connect AKS CLI With kubectl</h2>\n<h3 id=\"input\">INPUT</h3>\n<p><code>$ az aks get-credentials -g <resource group name> -n <aks name></code></p>\n<h3 id=\"expected-output\">Expected Output</h3>\n<pre><code class=\"language-bash\">Merged "lxz-aks" as current context in %HOME%\\.kube\\config\n</code></pre>\n<h2 id=\"make-sure-aks-cli-is-connected-with-kubectl\">Make Sure AKS CLI Is Connected With kubectl</h2>\n<h3 id=\"input\">Input</h3>\n<p><code>$ kubectl get nodes</code></p>\n<h3 id=\"expected-output\">Expected Output</h3>\n<pre><code class=\"language-bash\">NAME STATUS ROLES AGE VERSION\naks-nodepool-38987377-vmss000000 Ready agent 5d v1.20.9\n</code></pre>\n<h2 id=\"use-kubectl-to-update-image-version\">Use kubectl To Update Image Version</h2>\n<p>If <code>-n</code> or <code>--namespace</code> is not provided, <code>default</code> namespace will be applied by default.</p>\n<p><code>$ kubectl set image deployment [-n/--namespace] <deployment> <container>=<repo url>/<repo name>:<tag></code></p>\n<h3 id=\"input\">INPUT</h3>\n<p><code>$ kubectl set image deployment <deployment> <container>=<acr url>.azurecr.io/<image>:<tag></code></p>\n<h3 id=\"expected-output\">Expected Output</h3>\n<p>If return message is like down below, please provied <code>-n</code> with desired namespace.</p>\n<pre><code class=\"language-bash\">No resources found in default namespace.\n</code></pre>\n<p>Message shown if update is success.</p>\n<pre><code class=\"language-bash\">deployment.apps/<deployment name> image updated\n</code></pre>\n<p>If there is no message return, which means image has not been modified.</p>\n<h2 id=\"restart-pod\">Restart Pod</h2>\n<p>If return message is like down below, please provied <code>-n</code> with desired namespace.</p>\n<p><code>$ kubectl rollout restart [-n/--namespace] <resource type> <resource name></code></p>\n<h3 id=\"input\">INPUT</h3>\n<p><code>$ kubectl rollout restart -n <namespace> deployment <deployment name></code></p>\n<h3 id=\"expected-output\">Expected Output</h3>\n<pre><code class=\"language-bash\">deployment.apps/<deployment name> restarted\n</code></pre>\n<!--kg-card-end: markdown-->\n",
"date_published": "2021-12-24T18:52:47+10:00",
"url": "https://micro.benji.ltd/2021/12/24/aks-with-kubernetes.html"
},
{
"id": "https://benny.micro.blog/2021/11/17/dokku-on-digitalocean.html",
"title": "Dokku on DigitalOcean with Debian",
"content_html": "<!-- raw HTML omitted -->\n<p>wget <a href=\"https://raw.githubusercontent.com/dokku/dokku/v0.26.6/bootstrap.sh\">https://raw.githubusercontent.com/dokku/dokku/v0.26.6/bootstrap.sh</a></p>\n<p>sudo DOKKU_TAG=v0.26.6 bash bootstrap.sh</p>\n<h1 id=\"configure-your-server-domain-via-dokku-domainsset-global\">Configure your server domain via <code>dokku domains:set-global</code></h1>\n<h1 id=\"and-user-access-via-dokku-ssh-keysadd-to-complete-the-installationcodeprepcopy-and-paste-two-lines-starts-without-codecode-and-bang-dokku-is-ready-for-useph3-idsetting-up-dokku-and-global-domain-to-usesetting-up-dokku-and-global-domain-to-useh3phere-comes-a-little-difficult-part-set-the-ssh-access-you-may-wondering-why-we-have-to-do-this-its-because-some-of-the-docker-image-we-going-to-build-is-require-full-git-repository-files-in-the-same-folder-to-build-the-image-for-generate-the-configuration-file-use-the-assets-inside-the-folder-and-suchppanother-great-thing-with-dokku-is-you-can-push-the-repository-to-dokku-and-let-it-automatically-do-the-build-and-deploy-the-applicationpfigure-classkg-card-kg-code-cardprecode-classlanguage-bash-usually-your-key-is-already-available-under-the-current-users-sshauthorized_keys-file\">and user access (via <code>dokku ssh-keys:add</code>) to complete the installation<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->Copy and paste two lines starts without <!-- raw HTML omitted -->#<!-- raw HTML omitted --> and bang! Dokku is ready for use.<!-- raw HTML omitted --><!-- raw HTML omitted -->Setting up Dokku and Global Domain to use<!-- raw HTML omitted --><!-- raw HTML omitted -->Here comes a little difficult part, set the SSH access. You may wondering why we have to do this, it’s because some of the Docker image we going to build is require full git repository files in the same folder to build the image, for generate the configuration file, use the assets inside the folder and such.<!-- raw HTML omitted --><!-- raw HTML omitted -->Another great thing with Dokku is you can push the repository to Dokku, and let it “automatically” do the build and deploy the application.<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted --># usually your key is already available under the current user’s <code>~/.ssh/authorized_keys</code> file</h1>\n<p>cat ~/.ssh/authorized_keys | dokku ssh-keys:add admin</p>\n<h1 id=\"you-can-use-any-domain-you-already-have-access-to\">you can use any domain you already have access to</h1>\n<p>dokku domains:set-global dokku.me<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->Add SSH and global domain<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->After you ran the commands above, you should have Dokku set with SSH keys you use to access to droplet, and a domain Dokku will use for every apps. By default after you set global domain Dokku will use <!-- raw HTML omitted --><app_name>.domain.tld<!-- raw HTML omitted --> for every apps, this can also be change before and after you deploy the image.<!-- raw HTML omitted --><!-- raw HTML omitted -->Install Dokku plugins<!-- raw HTML omitted --><!-- raw HTML omitted -->We’ve setup our SSH keys and global domain, now we can install the plugins we need to use.<!-- raw HTML omitted --><!-- raw HTML omitted -->The essential plugins we are going to install is PostgresSQL / Redis / Letsencrypt.<!-- raw HTML omitted --><!-- raw HTML omitted -->PostgresSQL is required almost every modern application, Redis is widely use for cache and letsencrypt is for SSL certificate registrations.<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted --># dokku plugin:install <a href=\"https://github.com/dokku/dokku-postgres.git\">https://github.com/dokku/dokku-postgres.git</a> postgres</p>\n<h1 id=\"dokku-plugininstall-httpsgithubcomdokkudokku-redisgit-redis\">dokku plugin:install <a href=\"https://github.com/dokku/dokku-redis.git\">https://github.com/dokku/dokku-redis.git</a> redis</h1>\n<h1 id=\"dokku-plugininstall-httpsgithubcomdokkudokku-letsencryptgitcodeprefigcaptioninstallation-commands-for-pluginsfigcaptionfigurepand-now-your-droplet-is-ready-for-deploy-all-kinds-of-applicationsp\">dokku plugin:install <a href=\"https://github.com/dokku/dokku-letsencrypt.git\">https://github.com/dokku/dokku-letsencrypt.git</a><!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->Installation commands for plugins<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->And now your droplet is ready for deploy all kinds of applications!<!-- raw HTML omitted --></h1>\n",
"content_text": "<p>First thing have to worried about when it comes to self-host a bunch of micro-services is how you going to manage all these kinds of containers?</p><p>I came across <a href=\"https://dokku.com\">Dokku</a>, a docker powered mini-Heroku solution, best thing is you don't have to mess around all kinds of Docker commands and let Dokku manage most of the things for you, which includes database, letsencrypt, and domain management.</p><p>First thing I did is boot up a fresh droplet, since AMD is on trends nowadays I choose 2C4G Premium AMD droplet, also, if you can do all the things on Debian, why choose pre-built image on Digital Ocean with <a href=\"https://marketplace.digitalocean.com/apps/dokku\">\"slightly\" outdated Dokku</a>?</p><p>After droplet is ready, you need to install ufw, because Digital Ocean is not like AWS EC2 have prebuilt firewall rules, it's better for you set it up manually, ufw is also easy to set it up and solid for everyday usage. After that you should set ufw like this below for everything we need later.</p><figure class=\"kg-card kg-image-card kg-card-hascaption\"><img src=\"__GHOST_URL__/content/images/2021/11/image.png\" class=\"kg-image\" alt loading=\"lazy\" width=\"536\" height=\"380\"><figcaption>UFW settings</figcaption></figure><h2 id=\"install-dokku\">Install Dokku</h2><p>Trust me, it's <a href=\"https://dokku.com/#install-curl\">SUPER EASY</a>, everyone with basic copy-paste knowledge can install it, just follow the step. </p><pre><code class=\"language-bash\"> # for debian systems, installs dokku via apt-get\n\n wget https://raw.githubusercontent.com/dokku/dokku/v0.26.6/bootstrap.sh\n\n sudo DOKKU_TAG=v0.26.6 bash bootstrap.sh\n\n # Configure your server domain via `dokku domains:set-global`\n\n # and user access (via `dokku ssh-keys:add`) to complete the installation</code></pre><p>Copy and paste two lines starts without <code>#</code> and bang! Dokku is ready for use.</p><h3 id=\"setting-up-dokku-and-global-domain-to-use\">Setting up Dokku and Global Domain to use</h3><p>Here comes a little difficult part, set the SSH access. You may wondering why we have to do this, it's because some of the Docker image we going to build is require full git repository files in the same folder to build the image, for generate the configuration file, use the assets inside the folder and such.</p><p>Another great thing with Dokku is you can push the repository to Dokku, and let it \"automatically\" do the build and deploy the application.</p><figure class=\"kg-card kg-code-card\"><pre><code class=\"language-bash\"># usually your key is already available under the current user's `~/.ssh/authorized_keys` file\ncat ~/.ssh/authorized_keys | dokku ssh-keys:add admin\n\n# you can use any domain you already have access to\ndokku domains:set-global dokku.me</code></pre><figcaption>Add SSH and global domain</figcaption></figure><p>After you ran the commands above, you should have Dokku set with SSH keys you use to access to droplet, and a domain Dokku will use for every apps. By default after you set global domain Dokku will use <code><app_name>.domain.tld</code> for every apps, this can also be change before and after you deploy the image.</p><h3 id=\"install-dokku-plugins\">Install Dokku plugins</h3><p>We've setup our SSH keys and global domain, now we can install the plugins we need to use.</p><p>The essential plugins we are going to install is PostgresSQL / Redis / Letsencrypt.</p><p>PostgresSQL is required almost every modern application, Redis is widely use for cache and letsencrypt is for SSL certificate registrations.</p><figure class=\"kg-card kg-code-card\"><pre><code class=\"language-bash\"># dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres\n# dokku plugin:install https://github.com/dokku/dokku-redis.git redis\n# dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git</code></pre><figcaption>Installation commands for plugins</figcaption></figure><p>And now your droplet is ready for deploy all kinds of applications!</p>\n",
"date_published": "2021-11-17T00:01:45+10:00",
"url": "https://micro.benji.ltd/2021/11/17/dokku-on-digitalocean.html"
},
{
"id": "https://benny.micro.blog/2021/11/15/i-should-regain.html",
"title": "I should regain the control of my digital life, with Docker.",
"content_html": "<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "<!--kg-card-begin: markdown--><p>Some days ago after long working hours for almost a month streak, went back to work and start randomly browsing on network.</p>\n<p>And an idea just pop-up inside my head: "Why I have to use all these service and let the company controls my data?"</p>\n<p>So I decided to draw a blueprint, to regain all my digital life back to my hand.<br>\nBut do not get me wrong, I still use <a href=\"https://instagram.com/@issa.fooda\">Instaram</a> to share the things that I love, but not for most of the anything else.</p>\n<p>So this is the journey begins, first I choose AWS as my first try, because my work is already using Azure exclusively and it's well-known to me, so I decide why not give AWS a try?</p>\n<p>But things not went very well, big time.</p>\n<p>First of all is the pricing system, I spend nearly a week trying to figure out how much services is AWS offering and what those pricing plan is, but I decided if I'm ever going to do this, I'm going to do it <strong>NOT</strong> old fasioned way.</p>\n<p>My eyes got locked on on ECS, which makes Docker deployment easy and I don't have to worrried about any system management. because I already burned out at work, don't want to struggled that even on my own time.</p>\n<p>but just after some hours I figured that isn't that easy to handle ECS with Docker Hub only deployment, it's have some twist in it, because you still have to open an EC2 backend to run on it, even <a href=\"https://aws.amazon.com/lightsail/\">lightsail</a> is <strong>REALLY</strong> expensive for a single docker service that I want to host. compares to DigitalOcean or Vultr, it's basically a huge disadvantage.</p>\n<p>so I move my hosting platform on DigitalOcean.</p>\n<p>even though, I still need something to manage all kinds the service I want to host by myself, inside my head I have couple of services I want to host:</p>\n<ul>\n<li>Matrix\n<ul>\n<li>for IRC Bridge so I can ditch IRCCloud charges 5 bucks every months.</li>\n<li>Social Media Bridge, Instagram / Signal / Twitter DM and such</li>\n</ul>\n</li>\n<li>A federated Social Media\n<ul>\n<li>now days I rarely uses Twitter, already move to Mastodon, just cross-posting, why not just selfhost one and do it from there?</li>\n</ul>\n</li>\n<li>Git\n<ul>\n<li>HATE Microsoft, AAAAAAND gitlab. so I wish I can host Gitea.</li>\n</ul>\n</li>\n<li>Note Taking\n<ul>\n<li>already a big fan of Notion / GoodsNote 5, but I need something that can be sync / mobile friendly and self host all my data.</li>\n</ul>\n</li>\n<li>Wiki\n<ul>\n<li>can't count the times that googled some question and eventually finds the answer in search results that is on my <strong>OWN BLOG</strong>, so I need one sort of knowledge palace for it.</li>\n</ul>\n</li>\n<li>RSS reader\n<ul>\n<li>ever since GoogleReader died, moved to Freedly, but <a href=\"https://feedly.com/i/pro/welcome\">Freedly Pro</a> is quite expensive too.</li>\n</ul>\n</li>\n<li>Finacial Things,\n<ul>\n<li>well, I think this one is essential, because when doing tax most of the time I ended up to "Where all my money goes?"</li>\n</ul>\n</li>\n<li>A Blog. Which you are reading it right now.\n<ul>\n<li>I ended up going back to Ghost, just because it's the most painless thing to work with docker, it just works.</li>\n</ul>\n</li>\n</ul>\n<p>Well, the list is finished, so let's get right to it.</p>\n<!--kg-card-end: markdown-->\n",
"date_published": "2021-11-15T00:31:59+10:00",
"url": "https://micro.benji.ltd/2021/11/15/i-should-regain.html"
},
{
"id": "https://benny.micro.blog/2021/10/22/privacy.html",
"title": "Privacy",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>Wondering how Ghost fares when it comes to privacy and GDPR rules? Good news: Ghost does not use any tracking cookies of any kind.</p><p>You can integrate any products, services, ads or integrations with Ghost yourself if you want to, but it's always a good idea to disclose how subscriber data will be used by putting together a privacy page.</p>\n",
"date_published": "2021-10-22T03:51:41+10:00",
"url": "https://micro.benji.ltd/2021/10/22/privacy.html"
},
{
"id": "https://benny.micro.blog/2021/10/22/who-is-this.html",
"title": "Who is this dude?",
"content_html": "<!-- raw HTML omitted -->\n",
"content_text": "<p>I hate to define my self to someone who you sees on internet that <em>Love</em> to do something or <em>Doing</em> things that makes the world better.</p><p>My name is Benji, I'm a good dude, I was bad but now I'm good. Currently I'm Full-Stack Software Engineer / MIS / IT / DevOps / ... , my job is make all the computers and microservices function like it should be in a Food Production Company in Taiwan/Taipei.</p><p>In my spare time, I work on random side-projects on Github and Gitlab, help open source community to grow, big fan of IRC but since people are more focus on fedrated IRC-like service like Matrix.</p><h2 id=\"so-what-is-this-site-about\">So, what is this site about?</h2><p>This site is sharing all the thoughts, Ideas, and Blog my life. I've been on Ghost before, and man it changed a lot since version 1 and 2, now it is not like pure blogging platform now, but I tend to use it like one. Although it's a bit overkill using Ghost for blogging but yeah whatever. feel free to subscribe, there is no reason to but if you feel like want to receive all my thoughts ASAP, do it.</p><p>And I won't Spam, promise, <a href=\"https://www.mailgun.com/pricing/\">mailgun</a> is quite expensive :3</p>\n",
"date_published": "2021-10-22T03:51:39+10:00",
"url": "https://micro.benji.ltd/2021/10/22/who-is-this.html"
},
{
"id": "https://benny.micro.blog/2020/01/05/repair-azure-linux.html",
"title": "Repair Azure Linux VM Broken boot partition",
"content_html": "<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "<!--kg-card-begin: markdown--><h2 id=\"what-is-this-post-about\">What is this post about?</h2>\n<p>Couple of days ago some colleagues are complaining about unbearable performance of SAP Application which is currently host with OpenSUSE on Azure VM, shortly after my manager call me in on Monday morning and tells me to reboot <em>production</em> environment of SAP.</p>\n<p>Even though I know something will hit the fan because I surely did remember last time we reboot that machine, is 700+ days before.</p>\n<p>As expected VM stuck at closing db process for an hour and a half, after briefing my manager, he said "Hard reset." and it died, completely.</p>\n<blockquote>\n<p>I tried to stop it but NooOoOOooOOoo they don't listen to me!</p>\n</blockquote>\n<p>So this post is about how I spent 26 hours only depend on Microsoft messy documentation and try and error.</p>\n<p>Just in case I won't step into this mud again.</p>\n<p><strong>CAUTION</strong></p>\n<blockquote>\n<p>Perform such things like hard reset during a linux shutdown process will highly likely to damange your boot partition.</p>\n</blockquote>\n<h3 id=\"diagnose-problem\">Diagnose Problem</h3>\n<p>On Azure Portal find your VM and check the boot log by click the <strong>Serial Console</strong> on the left.</p>\n<p>If message like down below appears, you might have yourself a broken boot partition.</p>\n<pre><code>Failed to start File System Check on /dev/disk/by-uu...d-121f462d7e8d\n</code></pre>\n<h2 id=\"solution\">Solution</h2>\n<h3 id=\"prerequisite\">Prerequisite</h3>\n<h4 id=\"install-az-cli-extension\">Install az cli extension</h4>\n<blockquote>\n<p>Refer <a href=\"https://docs.microsoft.com/en-us/azure/virtual-machines/troubleshooting/repair-windows-vm-using-azure-virtual-machine-repair-commands\">Installation Guide</a></p>\n</blockquote>\n<h4 id=\"install-vm-repair-extension\">Install <code>vm-repair</code> Extension</h4>\n<p>If you haven't install this extension before.</p>\n<p><code>$ az extension add -n vm-repair</code></p>\n<p>If you already installed this extension, it's always a good idea to check extensions update.</p>\n<p><code>$ az extension update -n vm-repair</code></p>\n<h4 id=\"setup-repair-vm\">Setup Repair VM</h4>\n<p><code>$ az vm repair create -g {{MyResourceGroup}} -n {{myVM}} --repair-username {{username}} --repair-password {{password!234}} --verbose</code></p>\n<p>What this command will act is copy system partition files and mount it to a new VM which is <strong>automaticaly</strong> created.<br>\nWait for couple of minutes and you can ssh into your new VM.</p>\n<h2 id=\"start-repair\">Start Repair</h2>\n<h4 id=\"repair-command\">Repair Command</h4>\n<p>SSH into your repair VM, use <code>$ lsblk</code> to check the device Id of your broken parition and run:</p>\n<p><code>$ fsck /dev/{{device_name}}</code></p>\n<blockquote>\n<p><strong>Y</strong> on all questions.</p>\n</blockquote>\n<h4 id=\"unattened-script\">Unattened Script</h4>\n<p>Azure also provided a automatic repair script but I haven't tried it.</p>\n<p><code>$ az vm repair run –g {{MyResourceGroup}} –n {{MyVM}} -–run-on-repair --run-id 2 --verbose</code></p>\n<h2 id=\"complete-and-restore\">Complete and Restore</h2>\n<h3 id=\"delete-repair-vm\">Delete Repair VM</h3>\n<p>Use command down below to delere your repair VM.</p>\n<p><code>$ az vm repair restore -g {{MyResourceGroup}} -n {{MyVM}} --verbose</code></p>\n<p>This will replace the partition we just fixed to broken partition.</p>\n<p>And depends on your needs you can choose to keep or delete your repair VM, but it will still be charged even it's not booted.</p>\n<h2 id=\"boot-up-vm\">Boot up VM</h2>\n<p>Now you can boot up your VM with fixed boot partition.</p>\n<!--kg-card-end: markdown-->\n",
"date_published": "2020-01-05T10:00:00+10:00",
"url": "https://micro.benji.ltd/2020/01/05/repair-azure-linux.html"
},
{
"id": "https://benny.micro.blog/2019/12/13/macos-mssql.html",
"title": "在 macOS 上安裝 MSSQL 2019",
"content_html": "<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "<!--kg-card-begin: markdown--><h2 id=\"%E5%89%8D%E8%A8%80\">前言</h2>\n<p>這個週末參加了微軟的活動,在全場都是 Windows 筆電中我帶了一個 Mac 過去。 (汗<br>\n這個會議的主題是 MSSQL 以及 K8S 在 AZURE 上面的服務。<br>\n所以參加之前還擔心了一下環境的建置,不過也幸好微軟最近擁抱開源非常的勤勞,現在我們在 Docker 上面也可以安裝 MSSQL 了。<br>\n接下來就跟大家說明如何在 Mac 上面安裝 MSSQL 2019。</p>\n<h2 id=\"%E7%92%B0%E5%A2%83%E6%BA%96%E5%82%99\">環境準備</h2>\n<p>首先你要有 Docker Desktop,很值得慶幸的是現在 Docker 已經過了版本雜亂的年代了,<br>\n命令列中不再會有各種 <code>docker-engine</code>, <code>docker-compose</code> 還是什麼其他奇怪的命令讓使用搞不清楚狀況。</p>\n<h3 id=\"%E4%BD%BF%E7%94%A8-homebrew-%E5%AE%89%E8%A3%9D\">使用 homebrew 安裝</h3>\n<p>若正在看文章的你跟我一樣是活在 CLI 的世界裡面,我會推薦你使用這個方式安裝,能不開網頁就是不開!</p>\n<p><code>$ brew cask install docker</code></p>\n<h3 id=\"%E5%AE%98%E6%96%B9%E7%B6%B2%E7%AB%99%E4%B8%8B%E8%BC%89%E5%AE%89%E8%A3%9D\">官方網站下載安裝</h3>\n<p>若是你是屬於不喜歡 CLI 或者不熟悉的人,可以去 <a href=\"https://hub.docker.com/?overlay=onboarding\">官方網站(HUB)</a><br>\n下載 dmg 進行安裝。</p>\n<h2 id=\"%E5%AE%89%E8%A3%9D-mssql-2019\">安裝 MSSQL 2019</h2>\n<p>都安裝正常執行之後,我們就可以到官方的 Hub 去找微軟的官方 Repo 下載 Container。<br>\n<a href=\"https://hub.docker.com/_/microsoft-mssql-server\">Docker Hub Microsoft</a></p>\n<h2 id=\"pull-container\">pull container</h2>\n<p>在畫面中可以看到這個是微軟官方在維護的一個 Repo,將頁面往下可以看到所有目前釋放出來的 MSSQL 版本,<br>\n今天我們要瞄準的是 <code>MSSQL 2019</code></p>\n<p>在頭幾行就能夠看到若要下載 2019 的 MSSQL,我們需要下的指令為:<br>\n<code>$ docker pull mcr.microsoft.com/mssql/server:2019-latest</code></p>\n<h2 id=\"%E5%9F%B7%E8%A1%8C\">執行</h2>\n<p>下載下來之後我們應該要怎麼執行它?<br>\n很簡單,這部分我們可以直接一行指令搞定:</p>\n<pre><code class=\"language-bash\">docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=yourStrong(!)Password' -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-latest\n</code></pre>\n<h3 id=\"%E7%92%B0%E5%A2%83%E8%AE%8A%E6%95%B8\">環境變數</h3>\n<p>在這裡可以看到我們在執行的時候下了兩個 <code>-e</code> 的指令,這個指令代表著 Docker 在執行這個 Container 的時候所要設定的環境變數,<br>\n在官方的文檔中,最基本需要的環境變數為兩個:</p>\n<ul>\n<li>同意 EULA (ACCEPT_EULA)</li>\n<li>設定 SA 使用者的密碼 (SA_PASSWORD)</li>\n</ul>\n<p>SA 使用者的密碼可以隨便更改。</p>\n<h3 id=\"%E5%95%9F%E5%8B%95%E6%8C%87%E4%BB%A4\">啟動指令</h3>\n<p>在其他的部分可以看到還下了 Port 的對應,將 Container 的 1433 對應到本機的 1433 port,這個 port 為稍後連線的 Port,<br>\n還有更多的詳細指令可以在官方的文檔中找到。<a href=\"https://docs.docker.com/engine/reference/commandline/run\">Docker Run Options</a></p>\n<h2 id=\"%E5%AE%8C%E6%88%90\">完成</h2>\n<p>等候一下跑完成之後,輸入 <code>docker ps</code> 就能看到剛剛執行的 container 顯示在上面了。</p>\n<pre><code class=\"language-bash\">CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n6f6ee11802da mcr.microsoft.com/mssql/server:2019-latest "/opt/mssql/bin/perm…" 3 seconds ago Up 2 seconds 0.0.0.0:1433->1433/tcp hopeful_euler\n</code></pre>\n<p>這樣就代表成跑起來了</p>\n<h3 id=\"%E5%B0%8F%E6%8A%80%E5%B7%A7\">小技巧</h3>\n<p>如果想要看 container 的輸出訊息,去掉執行命令的 <code>-d</code> 就可以了,這樣在 Debug 的過程中會輕鬆很多。</p>\n<h2 id=\"%E5%90%8C%E5%A0%B4%E5%8A%A0%E6%98%A0\">同場加映</h2>\n<p>在 Mac 上如果想要連線到 MSSQL 該怎麼辦呢?<br>\n在 <code>SSMS</code> 沒有支援 Mac 的情況下,我們可以使用類似的工具進行連結。<br>\n這個工具就是微軟開發的 <code>Azure Data Studio</code> !</p>\n<h3 id=\"%E5%AE%89%E8%A3%9D-azure-database-studio\">安裝 Azure Database Studio</h3>\n<h4 id=\"homebrew-%E5%AE%89%E8%A3%9D\">Homebrew 安裝</h4>\n<p>直接執行<br>\n<code>$ brew cask install azure-data-studio</code></p>\n<h4 id=\"%E4%B8%80%E8%88%AC%E5%AE%89%E8%A3%9D\">一般安裝</h4>\n<p>前往 <a href=\"https://docs.microsoft.com/zh-tw/sql/azure-data-studio/download?view=sql-server-ver15\">Microsoft Docs</a><br>\n下載 Dmg 檔案進行安裝。</p>\n<h2 id=\"%E7%B5%90%E8%AA%9E\">結語</h2>\n<p>其實我一直都是不太喜歡微軟的,如果不是遊戲重度依賴 DirectX ,是連碰都不會碰的,但是出社會之後才漸漸理解到,<br>\n微軟在企業中的影響力是十分足夠的,但是微軟也沒有止步於此。 雖然剛開始跟 Linux 合作的時候被砲轟的滿天飛,<br>\n但微軟是真的證實有在為了開源做很大一部分的貢獻。</p>\n<p>也因為如此我們漸漸的可以在非微軟的平台上使用他們的工具,目前體驗最明顯的應該就是 .Net Core 了吧?<br>\n雖然還有很長的一段路要走,但是我期待有一天能夠在非微軟的平台上用微軟的工具開發。</p>\n<!--kg-card-end: markdown-->\n",
"date_published": "2019-12-13T10:00:00+10:00",
"url": "https://micro.benji.ltd/2019/12/13/macos-mssql.html"
},
{
"id": "https://benny.micro.blog/2019/03/03/100000.html",
"title": "中華電信小烏龜管理密碼",
"content_html": "<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "<!--kg-card-begin: markdown--><h1 id=\"%E5%89%8D%E8%A8%80\">前言</h1>\n<p>最近家裏裝了 DVR ,因為設定上的問題所以需要進去小烏龜設定密碼,發現原本的 <code>chtgpon</code> 密碼進不去了。<br>\n在網路上搜尋了一下發現因為資安的問題將密碼的機制更換了,利用系統更新的時候將舊的機子密碼也一起換掉。</p>\n<p>發現 P880 這台機子網路上已經有人放出來密碼的機制,但是光線機子 040-gw 卻沒有,按照同樣的模式嘗試之後果然讓我登入了。</p>\n<p>為了防止之後更新 DVR 主機的設定,整理了一下備存機子的管理員密碼。</p>\n<h1 id=\"i-040gw-%E5%85%89%E7%B7%9A%E5%B0%8F%E7%83%8F%E9%BE%9C\">I-040GW 光線小烏龜</h1>\n<p>因應資安問題,必須要在網頁上手動輸入 <code>https</code> 才能正確開啟網頁,<br>\n一開始怎麼樣都會拒絕連線,用 <code>nmap</code> 掃發現 <code>443</code> 是開的,還天真的以為不會這麼簡單吧。<br>\n使用者: <code>cht</code><br>\n密碼 : <code>40gw</code> + 小烏龜的 <code>MAC</code> 地址後四碼。</p>\n<p>如果不知道 <code>MAC</code> 地址可以跑去看機子的背面,如果真的很懶得去看就直接用 user <code>ssh</code> 進去打 <code>version</code> 看就好了。</p>\n<blockquote>\n<p><code>ssh user@router-ip</code> 密碼為 <code>user</code></p>\n</blockquote>\n<h1 id=\"p880-%E5%B0%8F%E7%83%8F%E9%BE%9C\">P880 小烏龜</h1>\n<p>使用者: <code>cht</code><br>\n密碼 : <code>p880</code> + 小烏龜的 <code>MAC</code> 地址後四碼。</p>\n<h1 id=\"%E7%B5%90%E8%AA%9E\">結語</h1>\n<p>照這個模式來看前面都是機子名稱的小寫後四個字 + 上 <code>MAC</code> 後四碼,記住之後就不會有問題了。</p>\n<p>所以我說那個撥號帳號為什麼要跟小額付款綁定在一起啊,麻煩死了。</p>\n<!--kg-card-end: markdown-->\n",
"date_published": "2019-03-03T10:00:00+10:00",
"url": "https://micro.benji.ltd/2019/03/03/100000.html"
},
{
"id": "https://benny.micro.blog/2019/01/31/fix-redshift-on.html",
"title": "Fix redshift on Manjaro",
"content_html": "<!-- raw HTML omitted -->\n<p>But not like f.lux being closed source, redshift is drivin by the community, so most users using linux is using redshift.<!-- raw HTML omitted --></p>\n<!-- raw HTML omitted -->\n",
"content_text": "<!--kg-card-begin: markdown--><p><a href=\"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/joins/redshift\">Redshift</a> is a software similar to <a href=\"https://justgetflux.com/\">f.lux</a><br>\nBut not like f.lux being closed source, redshift is drivin by the community, so most users using linux is using redshift.</p>\n<p>Both software actually funcintion the same, at night time it will tune down bluelight and makes your screen looks yellow / orange-ish color, reduce the bluelight ease your eyes on low-light environment, even improve your sleep quallity.</p>\n<h1 id=\"issue-of-redshift\">Issue of Redshift</h1>\n<p>On Windows, built-in low blue light settings is not that much adjustable, so I use f.lux on Windows, and redshift on my Macbook Pro, but recently an update broke something.</p>\n<pre><code>Trying location provider `geoclue2`...\nUsing provider `geoclue2`.\nUsing method `randr`.\nWaiting for initial location to become avaliable...\n GDBus.Error:org.freedesktop.DBus.Error.NoReply:\n Message recipient disconnected from message bus without replying.\nUnable to get location from provider.\n</code></pre>\n<p>The version that having this issue:</p>\n<pre><code>➜ pacman -Q geoclue2 redshift\ngeoclue2 2.5.1-1\nredshift 1.12-2\n</code></pre>\n<h1 id=\"solution\">Solution</h1>\n<p>After digging around I found you need to edit <code>/etc/geoclue/geoclue.conf</code>, and add</p>\n<pre><code>[redshift]\nallowed=true\nsystem=false\nusers=\n</code></pre>\n<p>at the bottom of your <code>~/.i3/config</code>, then we need to add:</p>\n<pre><code>exec --no-startup-id /usr/lib/geoclue-2.0/demos/agent\nexec --no-startup-id redshift-gtk\n</code></pre>\n<p>And everything should work as expected after a reboot.</p>\n<h1 id=\"refer\">Refer</h1>\n<p><a href=\"https://lobotuerto.com/blog/how-to-fix-redshift-in-manjaro-linux-i3/\">How to fix Redshift in Manjaro Linux i3</a></p>\n<!--kg-card-end: markdown-->\n",
"date_published": "2019-01-31T10:00:00+10:00",
"url": "https://micro.benji.ltd/2019/01/31/fix-redshift-on.html"
},
{
"id": "https://benny.micro.blog/2019/01/31/install-mbpfan-on.html",
"title": "Install Mbpfan on Arch Linux",
"content_html": "<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "<!--kg-card-begin: markdown--><h2 id=\"what-is-mbpfan\">What is <code>mbpfan</code></h2>\n<p>mbpfan is a background service controling your fan speed using coretemp.</p>\n<p>This software is design for user who is using linux on their Macbooks</p>\n<h2 id=\"install-on-arch-linux\">Install on Arch Linux</h2>\n<p>It's no-brainer one line command.</p>\n<p><code># yaourt -S mbpfan-git</code></p>\n<h2 id=\"activate-mbpfan-service-using-systemctl\">Activate <code>mbpfan</code> service using Systemctl</h2>\n<p><code># systemctl enable --now mbpfan.service</code><br>\n<code># systemctl start mbpfan.service</code></p>\n<h1 id=\"modify-mbpfan-settings\">Modify <code>mbpfan</code> Settings</h1>\n<p>Config file location should be at <code>/etc/mbpfan.conf</code>, you can adjust the fan curve via open it with your favorite editor.</p>\n<p>If you like me don't want your legs slowly cooked while you're using it on your lap, you can take my kind of aggressive config file as reference.</p>\n<pre><code>[general]\n# see https://ineed.coffee/3838/a-beginners-tutorial-for-mbpfan-under-ubuntu for the values\nmin_fan_speed = 2000\t# put the *lowest* value of "cat /sys/devices/platform/applesmc.768/fan*_min"\nmax_fan_speed = 6200\t# put the *highest* value of "cat /sys/devices/platform/applesmc.768/fan*_max"\nlow_temp = 45\t\t\t# try ranges 55-63, default is 63\nhigh_temp = 55\t\t\t# try ranges 58-66, default is 66\nmax_temp = 86\t\t\t# take highest number returned by "cat /sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_max", divide by 1000\npolling_interval = 1\t# default is 7 seconds\n</code></pre>\n<p>Apply your change via restart the service.</p>\n<p><code># systemctl restart mbpfan.service</code></p>\n<!--kg-card-end: markdown-->\n",
"date_published": "2019-01-31T10:00:00+10:00",
"url": "https://micro.benji.ltd/2019/01/31/install-mbpfan-on.html"
},
{
"id": "https://benny.micro.blog/2019/01/29/python.html",
"title": "Python 字典的多值用法",
"content_html": "<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<p>for key, value in dict.items():\nfor i in value:\nprint(i)\n<!-- raw HTML omitted --><!-- raw HTML omitted --></p>\n<!-- raw HTML omitted -->\n<p>for key, value in dict.items():\nfor i in value:\nif i == bar:\nreturn foo(key)\n<!-- raw HTML omitted --><!-- raw HTML omitted --></p>\n<!-- raw HTML omitted -->\n",
"content_text": "<!--kg-card-begin: markdown--><h1 id=\"%E5%89%8D%E8%A8%80\">前言</h1>\n<p>相信有在寫 QA、Bot 的人都會碰到類似的問題,當 input 語言是中文的時候,通常都會發生有模糊字詞代表同一個 keyword 的狀況。<br>\n前幾天就碰到了這個狀況,一開始原本想要用 <code>list</code> 來解決,但是在多個 keyword 的時候就不適用了,<br>\n所以只能用 <code>dict</code> 的方法來解決這個問題。</p>\n<h1 id=\"dict-%E7%9A%84%E7%B5%90%E6%A7%8B\">dict 的結構</h1>\n<p>Python 的 dict 基本結構這邊就不多做介紹了,隨便拜一下大神就有了,但是當你想要一個 key 有多個 value 的時候該怎麼處理呢?<br>\n這時候 dict 中的 list 就能派上用場了。</p>\n<h2 id=\"value-%E7%94%A8-list-%E4%BE%86%E5%84%B2%E5%AD%98\">value 用 list 來儲存</h2>\n<p>在設定 dict 的時候,每個目標的 keyword 把他當成 key 使用, values 則是儲存所有的 <code>alt</code> 也就是模糊字,<br>\n一般在使用字典的時候,格式美化之後應該是長這個樣子:</p>\n<pre><code>dict = {\n'key_a': 'value_a',\n'key_b': 'value_b',\n'key_c': 'value_c'\n}\n</code></pre>\n<p>這時候可以認知為 <code>key_a</code> 這個<strong>鍵</strong>配對的<strong>值</strong>為 <code>value_a</code>,但這樣子並不能滿足需求,所以我們可以讓 value 變成一個 list。</p>\n<pre><code>dict = {\n'key_a': ['alt_value_a','alt_value_b','alt_value_c'],\n'key_b': ['alt_value_d','alt_value_e','alt_value_f'],\n'key_c': ['alt_value_g','alt_value_h','alt_value_i']\n}\n</code></pre>\n<p>這樣子就符合我們的需求了,在一個 input 中有三個 keyword,而且每個 keyword 都有多個模糊字。</p>\n<h1 id=\"%E6%87%89%E7%94%A8\">應用</h1>\n<p>上面已經解決了百分之 50 的問題,再來就是剩下的百分之 50,在配對模糊字詞的時候,應該是把字詞抓出來,然後丟進迴圈配對,<br>\n在只有 list 的時候非常的好解決,只要這樣做就好了:</p>\n<pre><code>for i in list:\n if i == key:\n return i\n</code></pre>\n<p>但是在 dict 的世界中是不能這樣用的。</p>\n<h2 id=\"%E8%BF%B4%E5%9C%88-dict\">迴圈 dict</h2>\n<p>迴圈的時候,我比較喜歡使用 <code>.items()</code> 進行迴圈,<br>\n所以我們迴圈 dict 的時候,會用兩個變數做迴圈。</p>\n<pre><code>dict = {\n'key_a': ['alt_value_a','alt_value_b','alt_value_c'],\n'key_b': ['alt_value_d','alt_value_e','alt_value_f'],\n'key_c': ['alt_value_g','alt_value_h','alt_value_i']\n}\n\nfor key, value in dict.items():\n for i in value:\n print(i)\n</code></pre>\n<p>這樣子做的時候,代表會 loop 第一個 key 跟 value,當 value 的迴圈結束了之後,才會進行下一個 key 的迴圈。<br>\n在一般 dict 的狀況下, value 只有一個,但現在我們把 value 用 list 儲存,所以我們可以再加上一個針對 value list 的迴圈。<br>\n結果的 output 會是這個樣子:</p>\n<pre><code>alt_value_a\nalt_value_b\nalt_value_c\nalt_value_d\nalt_value_e\nalt_value_f\nalt_value_g\nalt_value_h\nalt_value_i\n</code></pre>\n<p>可以發現他把全部的 list 都迴圈一次了,就可以開始做模糊字詞的搜尋了。</p>\n<h2 id=\"%E5%BE%9E-value-%E6%89%BE-key\">從 value 找 key</h2>\n<p>如果在 value 的 list 中比對到了模糊字詞,要怎麼樣把他返回正確的 keyword 讓後續的程式處理才好?<br>\n在上面的程式碼中,可以看到我們一開始設定的變數裡面有一個 key,這個變數可以直接輸出當前 key 的名稱。<br>\n所以稍作修改之後只要把 key 丟出去就好了:</p>\n<blockquote>\n<p>bar = 要查詢的模糊字<br>\nfoo = 要丟出去的 function 名稱</p>\n</blockquote>\n<pre><code>dict = {\n'key_a': ['alt_value_a','alt_value_b','alt_value_c'],\n'key_b': ['alt_value_d','alt_value_e','alt_value_f'],\n'key_c': ['alt_value_g','alt_value_h','alt_value_i']\n}\n\nfor key, value in dict.items():\n for i in value:\n if i == bar:\n return foo(key)\n</code></pre>\n<p>這樣就大功告成啦~</p>\n<h1 id=\"%E5%BF%83%E5%BE%97\">心得</h1>\n<p>我忘記是什麼時候開始喜歡使用 dict 的,我只記得為了搞清楚 dict 的用法跟 list 中調用的區分就花了好多時間去自己摸索、理解,<br>\n甚至是到最後的活用都花了不少時間,不過一切都理解了之後就覺得 dict 其實是個</p>\n<blockquote>\n<p>平常的時候不知道該怎麼用,知道了之後事半功倍</p>\n</blockquote>\n<p>的一個功能。</p>\n<p>之前有幸能夠去某知名大學旁聽的時候發現好像很多新手都對 dict 的處理非常的棘手,教授也因為眾多學生提出的問題一時之間也沒辦法解釋清楚,<br>\n導致學生一片懵逼、作業繳交率極低,我才起了這篇文章的想法但一直沒寫,直到前幾天真的碰到了這個問題,就決定寫下來了。<br>\n也不是在批評還是什麼的,因為網路上的文章都說的非常零散,<em>或者沒找對地方</em>,就算是官方的 docs 也是非常難懂,<br>\n加上我這個人是從<code>範例</code>中學習的人,給我很多文字我根本看不下去,直接給我範例讓我修改、從 debug 中學習,直到自己能夠寫出來才是我認為學東西最快的方式。<br>\n在這裏已經盡了自己最大的努力解釋其中的用法了,希望有幫助到看到這裡的您。</p>\n<!--kg-card-end: markdown-->\n",
"date_published": "2019-01-29T10:00:00+10:00",
"url": "https://micro.benji.ltd/2019/01/29/python.html"
},
{
"id": "https://benny.micro.blog/2018/12/15/iphone.html",
"title": "蘋果最好的產品是隱私,而不是 iPhone",
"content_html": "<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n",
"content_text": "<!--kg-card-begin: markdown--><p>Source:<a href=\"https://hackernoon.com/apples-best-product-is-privacy-not-iphone-9919f5fc6fb7\">https://hackernoon.com/apples-best-product-is-privacy-not-iphone-9919f5fc6fb7</a></p>\n<h1 id=\"%E5%89%8D%E8%A8%80\">前言</h1>\n<p>在我手機裡面的資料(相片、聯絡人、行程資料、電子郵件、訊息)完全展現了我這個人,以及我未來的行程計畫。<br>\n當然在此之外我也同時在使用 Facebook、Instagram、Twitter、LinkedIn。<br>\n而相信大家都知道,這些大型公司像是 Facebook、Amazon、Google 正在一點一滴的利用他們機器學習的算法,<br>\n藉由讀取我們的 訊息、照片、聯絡人 來奪取我們的隱私。你的數位生活完全被窺探、無一例外。<br>\n你的地理位置、IP 位置、文件、Excel 表格也不會像你想的一樣安全。<br>\n這些資料很可能在你不知情的情況下被賣給第三方企業。</p>\n<p>你可以想像一下你和你家人、可愛的小孩一點一滴的紀錄都在你完全不知道的情況下被公開嗎?<br>\n沒有人想要自己的私人生活以及紀錄變成別人商業模式中的一部分。</p>\n<p>很不辛的是,那些大型科技公司正在做這樣的事情。</p>\n<hr>\n<h1 id=\"%E9%A9%97%E8%AD%89\">驗證</h1>\n<p>最近我從 Facebook、Google、Apple 下載了我的備份資料、你也可以這樣做。<br>\n其實過程非常簡單,這些是教學連結:</p>\n<blockquote>\n<p>因為文章的語言,已經轉變成中文教學</p>\n</blockquote>\n<ul>\n<li><a href=\"https://free.com.tw/facebook-download-your-information/\">下載備份 Facebook 的所有訊息、相片和對話紀錄</a></li>\n<li><a href=\"https://support.google.com/accounts/answer/3024190?hl=zh-Hant\">下載您的資料 Google 帳戶說明 - Google Support</a></li>\n<li><a href=\"https://privacy.apple.com\">下載 Apple 資料拷貝(需登入)</a></li>\n</ul>\n<p>Facebook 和 Google 只花了差不多一個小時就把資料準備好讓我下載,<em>但是蘋果差不多耗時一個禮拜。</em></p>\n<p>從 Facebook 取得的壓縮檔案差不多 <code>144 MB</code>、Google 的容量為 <code>5.03 GB</code>!<br>\n蘋果給我的資料不到 <code>5 MB</code>。</p>\n<p>我被 Facebook 的資料備份裡面看到的內容嚇到了,我在裡面找到了我刪除很多年的聯絡人資料,<br>\n我還看到了 Facebook 知道我的人生任何一個時期所發生的事情,他把我的聯絡人資料給了非常多的公司;<br>\n那些公司我有大部分甚至都沒聽說過他們的名字!<br>\n從我剛開始加入 Facebook 開始發送的任何訊息,Facebook 都完整的儲存了,隱私在這個平台來說根本不存在。我甚至放棄下載 Google 的資料備份,因為我瞭解到會有什麼樣子的東西出現在裡面,依照檔案的大小來看我可以很確定裡面的內容一定比 Facebook 可怕十倍。</p>\n<p>我瞭解到我必須要刪除我的電子痕跡並且開始使用一些更注重用戶隱私的平台,<del>我將在下篇文章寫出如何在這個數位時代擁有更多的安全和隱私。</del> <sub>譯:並不存在,這作者寫完就去關心環保議題了<sub></p>\n<h1 id=\"%E8%98%8B%E6%9E%9C-vs-google-%E9%9A%B1%E7%A7%81\">蘋果 vs Google 隱私</h1>\n<p>下方的圖片顯示出 Google 到底連結了多少資料到他們伺服器上:<br>\n<a href=\"https://privacy.google.com/your-data.html\">Google 官方頁面</a><br>\n<img src=\"uploads/2023/2d23c89126.jpg\" alt=\"Google Privacy Page\" loading=\"lazy\"><br>\n<a href=\"https://www.apple.com/in/privacy\">Apple 官方頁面</a><br>\n<img src=\"https://cdn-images-1.medium.com/max/1600/1*dn0r-ggeYUQavH-90Rm4Zw.png\" alt=\"Apple Privacy Page\" loading=\"lazy\"></p>\n<p>光是跟 蘋果 比對他們的隱私權策略,結果實在是讓人驚恐無比!<br>\nGoogle 直接寫出了他們會閱讀你的 Email, 蘋果則表示就算是你的<strong>心跳頻率</strong>都是不對外公開的。</p>\n<blockquote>\n<p>坦白來說,如果消費者是我們的產品,我們可以利用監視所有我們<em>產品</em>的資料來獲得巨大的財富。<br>\n但是我們決定不這麼做 - Tim Cook</p>\n</blockquote>\n<p>我花了一點時間閱讀了蘋果的隱私權策略,下面是一個段落的擷取,我相信你們會喜歡裡面的內容:<br>\n<img src=\"https://cdn-images-1.medium.com/max/1600/1*pXiqHnHrr7o0zMIgaVwjkw.png\" alt=\"Apple's Privacy Policy\" loading=\"lazy\"><br>\n<img src=\"uploads/2023/c38274be3a.jpg\" alt=\"Apple's Privacy Policy\" loading=\"lazy\"></p>\n<hr>\n<p>在 iMessage 和 FaceTime 的通訊,使用的是 <strong>點對點加密</strong> ,這代表就算是蘋果也沒辦法讀取其中的訊息。<br>\n蘋果甚至不儲存可以辨識出使用者的 <strong>地理位置</strong>、<strong>地圖搜尋紀錄</strong>、<strong>Siri 的請求內容</strong></p>\n<p>最多蘋果擁有消費者的訊息,就是 iTunes、App Store、有關於手機的使用狀況(bug analyzing),蘋果並不像是 Google 和 Facebook 儲存任何 訊息、地理位置、以及廣告。</p>\n<p>如果看得更仔細一些,你會發現蘋果甚至不讓 NSA 存取消費者的 iCloud 鑰匙,保護了每個使用他們產品的員工以及消費者,而且並<strong>沒有為任何執法單位安插任何後門在任何系統中。</strong></p>\n<p>在最近的新聞中,顯示出蘋果甚至會<strong>打亂 Wifi 的 MAC 地址</strong>,讓那些廣告主無法在公共場合中定位消費者的位置,這是一個非常小的功能,非常多的 iOS 使用者甚至不知道有這個功能默默的保護著他們的隱私。</p>\n<p>機器學習的算法需要非常大型的資料庫來顯示出精確的結果,蘋果使用了不同於其他公司的隱私權政策以及彷彿來自未來的算法,獲得了令人印象深刻的結果。<br>\n蘋果的差異性隱私是一種基於加密的實踐,用於獲取使用者的偏好數據,而無需使用者的完整資料導入。</p>\n<h1 id=\"%E7%B5%90%E8%AB%96\">結論</h1>\n<p>隨著 iOS、OS X、iCloud 的更新,蘋果一直嘗試著加強他們的隱私權政策,iOS 設計成不會去侵犯到使用者的隱私,原廠預設的鍵盤不會偷偷的傳送輸入的資料到網路上_(A陣營表示:)_而且蘋果提供的 HomeKit 服務完整的確保了用戶的隱私權在他們的控制之下、Safari 提供了預設搜尋引擎為 <strong>DuckDuckGo</strong> 的選項(目前搜尋引擎業界中隱私最好的一個服務)<br>\n<em><strong>TouchID、FaceID 的資料則儲存在硬體晶片中(T1、T2 安全晶片)。</strong></em></p>\n<hr>\n<blockquote>\n<p>就算蘋果的產品<strong>非常</strong>昂貴,但是蘋果提供最好的產品卻是<em><strong>無價</strong></em>的。</p>\n</blockquote>\n<!--kg-card-end: markdown-->\n",
"date_published": "2018-12-15T10:00:00+10:00",
"url": "https://micro.benji.ltd/2018/12/15/iphone.html"
},
{
"id": "https://benny.micro.blog/2018/12/07/a-bitmessage-spam.html",
"title": "A BitMessage Spam bot written by Python",
"content_html": "<!-- raw HTML omitted -->\n<p>def get_address():\nclient_address = json.loads(api.listAddresses())\ntarget_address = []\ny = 0</p>\n<pre><code>for x in client_address['addresses']:\n target_address.append(client_address['addresses][y]['address'].encode('ASCII', 'ignore'))\n y += 1\n \nreturn target_address\n</code></pre>\n<p>if <strong>main</strong> == “<strong>name</strong>”:\napiUri = “https://username:[email protected]:8442”\napi = xmlrpclib.ServerProxy(apiUri)\nprint get_address()<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->After can printed all the addresses, we need to work on the spam part.<!-- raw HTML omitted --><!-- raw HTML omitted -->Send Message<!-- raw HTML omitted --><!-- raw HTML omitted -->On <!-- raw HTML omitted -->official API docs<!-- raw HTML omitted --> send message through API is fairly easy, encode desire message to <!-- raw HTML omitted -->base64<!-- raw HTML omitted --> and you are able to send the request to API. However, this is not a good spam bot, it require to modify hardcoded message in order to spam a new message. Proper spam bot is needed to edit message on the fly while program is running, so we need to make a text file to store messages. I make a <!-- raw HTML omitted -->content.txt<!-- raw HTML omitted --> to store spam message.<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->This is title, I’m going to spam message.\nThis is the message body<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->In order to read this file, we need to write a new method.<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->def send_message():\nwith open(‘content.txt’, ‘r+’) as f:\ncon = f.read().split(’\\n’, 1)\nsubject = con[0]\ncontent = con[1]\nsubject = subject.encode(‘utf-8’).strip()\nsubject_data = base64.b64encode(subject)\ncontent = content.encode(‘utf-8’).strip()\ncontent_data = base64.b64encode(content)</p>\n<pre><code>while True:\n\tfor j in addrs:\n \tack_data = api.sendMessage(j, j, subject_data, content_data, 2)\n print 'Sent:', ackData\n time.sleep(2)\n print 'All address are looped, sleep 3600 seconds.'\n time.sleep(3600)\nreturn</code></pre><p>And now you have your simple spam bot.</p><h3 id="polish-the-code">Polish the Code</h3><p>Since the end game is distribute to cross-platform, we need to make our little spam bot able to self-generate config file and content text file. </p><pre><code class="language-python">import os\n</code></pre>\n<p>import sys\nfrom os import path, environ</p>\n<p>def get_config_folder():\napp_folder = “PyBitmessage”\ndata_folder = None\nif “BITMESSAGE_HOME” in environ:\ndata_fonder = environ[“BITMESSAGE_HOME”]\nif data_fonder[-1] not in [os.path.sep, os.path.altsep]:\ndata_folder += os.path.sep\nelif sys.platform == ‘darwin’:\nif “HOME” in environ:\ndata_folder = path.join(os.environ[“HOME”], “Library/Application Support/”, app_folder) + ‘/’\nelif sys.platform == ‘win32’:\ndata_folder = path.join(environ[“APPDATA”].decode(sys.getfilesystemencoding(), ‘ignore’), app_folder) + path.sep\nelse:\ntry:\ndata_folder = path.join(environ[“XDG_CONFIG_HOME”], app_folder)\nexcept KeyError:\ndata_folder = path.join(environ[“HOME”], “.config”, app_folder)</p>\n<pre><code>return data_folder</code></pre><p>Using method above we can retrieve <code>keys.dat</code> from almost every platforms. Then we need to retrieve api config from it. To be noted <code>keys.dat</code> have same format of <code>ConfigParser</code>. After little copy and paste, result should somewhat like this.</p><pre><code class="language-python">import os\n</code></pre>\n<p>import sys\nfrom os import path, environ</p>\n<p>def get_config_folder():\napp_folder = “PyBitmessage”\ndata_folder = None\nif “BITMESSAGE_HOME” in environ:\ndata_fonder = environ[“BITMESSAGE_HOME”]\nif data_fonder[-1] not in [os.path.sep, os.path.altsep]:\ndata_folder += os.path.sep\nelif sys.platform == ‘darwin’:\nif “HOME” in environ:\ndata_folder = path.join(os.environ[“HOME”], “Library/Application Support/”, app_folder) + ‘/’\nelif sys.platform == ‘win32’:\ndata_folder = path.join(environ[“APPDATA”].decode(sys.getfilesystemencoding(), ‘ignore’), app_folder) + path.sep\nelse:\ntry:\ndata_folder = path.join(environ[“XDG_CONFIG_HOME”], app_folder)\nexcept KeyError:\ndata_folder = path.join(environ[“HOME”], “.config”, app_folder)</p>\n<pre><code>return data_folder\n</code></pre>\n<p>def get_bm_config(setting_key):\nvalue = None\ntry:\nvalue = cp.get(settings_section, setting_key)\nexcept Exception as e:\npass\nreturn value</p>\n<p>if <strong>main</strong> == “<strong>name</strong>”:\ncp = ConfigParser.SafeConfigParser()\ncp.read(get_config_fonder() + ‘keys.dat’)<!-- raw HTML omitted --><!-- raw HTML omitted --><!-- raw HTML omitted -->After combine both config retrieve and send message code, this spam bot is ready for production.<!-- raw HTML omitted --><!-- raw HTML omitted -->Conclusion<!-- raw HTML omitted --><!-- raw HTML omitted -->I have made couple of spam bots, basically all bots does the same thing, same concept. The idea of using blockchain to deliver message is paradox, it is good to be fully anonymous when you trying to say something on the internet. However, this concept can also be easily adopt by criminals. Overall I think even though Bitmessage have come so far, there is still a long way to go.<!-- raw HTML omitted --><!-- raw HTML omitted -->About Bitmessage’s POW (Proof-of-Work) even at this point writing this article, I have not figure it out how this system worked. I tried to browse someone elses code on send message part they are not using that method too. At first I did not think my bot will be working without doing POW before message sending out, but I do get some replies from Chans that complaining the dummy message I sent. Hence I guess this worked. :P<!-- raw HTML omitted --></p>\n",
"content_text": "<blockquote>BitMessage is a anonymous messaging app which uses blockchain to communicate. There is gotta be some evil things I can do with it. - 22 yo me.</blockquote><h2 id=\"why\">Why?</h2><p>Recently I been studing <a href=\"https://en.wikipedia.org/wiki/Blockchain\">Blockchain</a> and I remembered there is a messenger app called <a href=\"https://en.wikipedia.org/wiki/Bitmessage\">BitMessage</a> which is used by activisms during protests a while ago. So I download it back to my laptop to see what has changed since. If you are not familar with BitMessage, it is a really cool application lives on blockchain, you can find more useful and accurate information on their <a href=\"https://wiki.bitmessage.org/\">wiki</a> page.</p><p>After surfing around their Github I found that they have API built into client allows other program to communicate with the client. So, why not try to spam this specific protocol to see what blockchain can prevent with spam bot?</p><h2 id=\"lets-spam-it\">Let's Spam It!</h2><h3 id=\"environment\">Environment</h3><p>First, we are going to use <a href=\"https://github.com/Bitmessage/PyBitmessage/releases/\">PyBitmessage</a> to be our API Proxy. Basically it runs on all platforms because of Python + QT, pick whatever operation system you have and let's roll!</p><p>First of all we need to open our newly downloaded Bitmessage client and let it sync through the data. Sync is finished if you sees <code>Object(s) to be synced = 0</code>, next we need to retrieve <code>keys.dat</code>. Normally <code>keys.dat</code> is bundled within the application. Go to <code>Settings</code> -> <code>Settings</code> and check the <code>Run in Portable Mode</code>. After you close the application, <code>keys.dat</code> should appear inside the folder.</p><h3 id=\"enable-api-proxy\">Enable API Proxy</h3><p>Use your favorite editor to open <code>keys.dat</code>, add following information.</p><pre><code class=\"language-conf\">apiport = 8442\napiinterface = 127.0.0.1\napiuseranme = <desireusername>\napipassword = <desirepassword></code></pre><p>After that, open Bitmessage again and uncheck the <code>Run in Portable Mode</code>, you can let it keep running in the background.</p><h2 id=\"the-making-of-spam-bot\">The Making of Spam Bot</h2><p>Bitmessage API is using <code>xmlrpc</code> to communicate according to API docs. We can use simple Python console to test the connection.</p><pre><code class=\"language-python\">apiUri = \"https://username:[email protected]:8442\"\napi = xmlrpclib.ServerProxy(apiUri)\n</code></pre><p>After connection is established you can use simple <code>add</code> method to check if client can start handle requests.</p><pre><code>>>>> api.add(2,3)\n5\n>>>> </code></pre><h3 id=\"retrieve-chan-addresses\">Retrieve Chan Addresses</h3><p>To make the spam bot have most converages of users, target to Chans is the best idea. First we need to retrieve all address which I pre-added to Bitmessage. Example code will be something like below</p><pre><code class=\"language-python\">import xmlrpclib\nimport json\n\ndef get_address():\n client_address = json.loads(api.listAddresses())\n target_address = []\n y = 0\n \n for x in client_address['addresses']:\n target_address.append(client_address['addresses][y]['address'].encode('ASCII', 'ignore'))\n y += 1\n \n return target_address\n\nif __main__ == \"__name__\":\n\tapiUri = \"https://username:[email protected]:8442\"\n\tapi = xmlrpclib.ServerProxy(apiUri)\n\tprint get_address()</code></pre><p>After can printed all the addresses, we need to work on the spam part.</p><h3 id=\"send-message\">Send Message</h3><p>On <a href=\"https://bitmessage.org/wiki/API_Reference\">official API docs</a> send message through API is fairly easy, encode desire message to <code>base64</code> and you are able to send the request to API. However, this is not a good spam bot, it require to modify hardcoded message in order to spam a new message. Proper spam bot is needed to edit message on the fly while program is running, so we need to make a text file to store messages. I make a <code>content.txt</code> to store spam message.</p><pre><code>This is title, I'm going to spam message.\nThis is the message body</code></pre><p>In order to read this file, we need to write a new method.</p><pre><code class=\"language-python\">def send_message():\n\twith open('content.txt', 'r+') as f:\n con = f.read().split('\\n', 1)\n subject = con[0]\n content = con[1]\n subject = subject.encode('utf-8').strip()\n subject_data = base64.b64encode(subject)\n content = content.encode('utf-8').strip()\n content_data = base64.b64encode(content)\n \n while True:\n \tfor j in addrs:\n \tack_data = api.sendMessage(j, j, subject_data, content_data, 2)\n print 'Sent:', ackData\n time.sleep(2)\n print 'All address are looped, sleep 3600 seconds.'\n time.sleep(3600)\n return</code></pre><p>And now you have your simple spam bot.</p><h3 id=\"polish-the-code\">Polish the Code</h3><p>Since the end game is distribute to cross-platform, we need to make our little spam bot able to self-generate config file and content text file. </p><pre><code class=\"language-python\">import os\nimport sys\nfrom os import path, environ\n\ndef get_config_folder():\n\tapp_folder = \"PyBitmessage\"\n data_folder = None\n if \"BITMESSAGE_HOME\" in environ:\n \tdata_fonder = environ[\"BITMESSAGE_HOME\"]\n if data_fonder[-1] not in [os.path.sep, os.path.altsep]:\n \tdata_folder += os.path.sep\n elif sys.platform == 'darwin':\n \tif \"HOME\" in environ:\n \tdata_folder = path.join(os.environ[\"HOME\"], \"Library/Application Support/\", app_folder) + '/'\n elif sys.platform == 'win32':\n \tdata_folder = path.join(environ[\"APPDATA\"].decode(sys.getfilesystemencoding(), 'ignore'), app_folder) + path.sep\n else:\n \ttry:\n \tdata_folder = path.join(environ[\"XDG_CONFIG_HOME\"], app_folder)\n except KeyError:\n \tdata_folder = path.join(environ[\"HOME\"], \".config\", app_folder)\n \n return data_folder</code></pre><p>Using method above we can retrieve <code>keys.dat</code> from almost every platforms. Then we need to retrieve api config from it. To be noted <code>keys.dat</code> have same format of <code>ConfigParser</code>. After little copy and paste, result should somewhat like this.</p><pre><code class=\"language-python\">import os\nimport sys\nfrom os import path, environ\n\ndef get_config_folder():\n\tapp_folder = \"PyBitmessage\"\n data_folder = None\n if \"BITMESSAGE_HOME\" in environ:\n \tdata_fonder = environ[\"BITMESSAGE_HOME\"]\n if data_fonder[-1] not in [os.path.sep, os.path.altsep]:\n \tdata_folder += os.path.sep\n elif sys.platform == 'darwin':\n \tif \"HOME\" in environ:\n \tdata_folder = path.join(os.environ[\"HOME\"], \"Library/Application Support/\", app_folder) + '/'\n elif sys.platform == 'win32':\n \tdata_folder = path.join(environ[\"APPDATA\"].decode(sys.getfilesystemencoding(), 'ignore'), app_folder) + path.sep\n else:\n \ttry:\n \tdata_folder = path.join(environ[\"XDG_CONFIG_HOME\"], app_folder)\n except KeyError:\n \tdata_folder = path.join(environ[\"HOME\"], \".config\", app_folder)\n \n return data_folder\n\ndef get_bm_config(setting_key):\n\tvalue = None\n try:\n \tvalue = cp.get(settings_section, setting_key)\n except Exception as e:\n \tpass\n return value\n\nif __main__ == \"__name__\":\n\tcp = ConfigParser.SafeConfigParser()\n cp.read(get_config_fonder() + 'keys.dat')</code></pre><p>After combine both config retrieve and send message code, this spam bot is ready for production.</p><h2 id=\"conclusion\">Conclusion</h2><p>I have made couple of spam bots, basically all bots does the same thing, same concept. The idea of using blockchain to deliver message is paradox, it is good to be fully anonymous when you trying to say something on the internet. However, this concept can also be easily adopt by criminals. Overall I think even though Bitmessage have come so far, there is still a long way to go.</p><p>About Bitmessage's POW (Proof-of-Work) even at this point writing this article, I have not figure it out how this system worked. I tried to browse someone elses code on send message part they are not using that method too. At first I did not think my bot will be working without doing POW before message sending out, but I do get some replies from Chans that complaining the dummy message I sent. Hence I guess this worked. :P</p>\n",
"date_published": "2018-12-07T10:00:00+10:00",
"url": "https://micro.benji.ltd/2018/12/07/a-bitmessage-spam.html"
}
]
}