Compare commits
No commits in common. "85346865fb8e6c7a1bf2f644cee5cc7c9271b5bb" and "9138f8a960d951a7a26c11237a852e53d6a8fde1" have entirely different histories.
85346865fb
...
9138f8a960
14 changed files with 0 additions and 515 deletions
Binary file not shown.
Before Width: | Height: | Size: 999 KiB |
Binary file not shown.
Before Width: | Height: | Size: 477 KiB |
Binary file not shown.
Before Width: | Height: | Size: 36 KiB |
Binary file not shown.
Before Width: | Height: | Size: 242 KiB |
Binary file not shown.
Before Width: | Height: | Size: 53 KiB |
|
@ -1,13 +0,0 @@
|
|||
Created: 2024-07-15 12:20:00
|
||||
Updated: 2024-07-15 12:20:00
|
||||
Title: Blazor Gotchas I Am Frustrated By
|
||||
Slug: blazor-gotchas-i-am-frustrated-by
|
||||
Description: Blazor is a wonderful looking technology but unfortunately it has some pain points.
|
||||
Tags:
|
||||
- rants
|
||||
- blazor
|
||||
- c#
|
||||
- dotnet
|
||||
- programming
|
||||
- software
|
||||
- web
|
|
@ -1,159 +0,0 @@
|
|||
## Intro
|
||||
For as long as browsers have existed, there has been one and only one programming language that has dominated developing the web: JavaScript. And as long as there has been JavaScript, there's been bemoaning and complaints about the inadequacies and inconsistencies of the language. Some of it is warranted. Some of it is not. Regardless, many an individual, companies and the like have attempted to replace JavaScript with something else. Something better.
|
||||
|
||||
Blazor is Microsofts latest (more on that in a moment) attempt to leverage their existing Dotnet ecosystem to do just that: make JavaScript obsolete and write web applications and websites with C# and Dotnet.
|
||||
|
||||
## Brief History Of JavaScript Replacements
|
||||
Replacement attempts to JavaScript are not a new concept. It has been attempted probably almost as soon as the language gained prominence in the mid to late 90s. The late 90s and early 00s saw the rise and fall of Java applets: tiny applications written in Java that could be embedded into a website as a standalone component. The early 2000s introduced the world to Flash and due to the multimedia capabilities (none of which was present at the time in html, css or JavaScript) it became a very popular way of displaying animated and lively content. Actionscript, a close cousin to JavaScript, allowed even further power to developers. Most people probably remember games and video players from that era that popped up pretty much everywhere. Flash sizzled out as a technology in less than a decade mostly, I argue, due to html 5 and the release of Google Chrome and it's V8 engine.
|
||||
|
||||
In the same time frame Flash was getting notoriety, Microsoft released its first attempt at a solution in the form of Silverlight. Silverlight was essentially the same concept Blazor leverages today: Write your code in C# and xaml without any need to write JavaScript. However, due to poor adoption by browsers and emergence of html 5 put a cap in that technology quickly.
|
||||
|
||||
Microsoft's second attempt came through ActiveX but unlike Silverlight, ActiveX allowed a broader range of programming languages. Security issues and decline in Internet Explorer (the only browser truly supporting the technology) popularity proved the downfall of this attempt as well.
|
||||
|
||||
Others too, tried and failed. Some attempted to replace JavaScript entirely with different languages. Others opted for a halfway solution via extensions to JavaScript itself like CoffeeScript and more recently TypeScript. JavaScript development itself transformed as well to a point that few these days write it raw, opting to use frameworks such as React or Angular. Then came Blazor.
|
||||
|
||||
## About Blazor
|
||||
Somewhere in 2010s, the existing .NET was deemed unsuitable for future development. Or so I assume. Regardless, a new framework arose alongside it called .NET Core. By the time version 3.1 had come out, a decision was made to merge the two into a single unified .NET, which later would be released as .NET 5, skipping version 4 entirely (Microsoft likes to skip numbers).
|
||||
|
||||
During the Core era in 2017, Blazor arose as an experiment in Oslo by Steve Sanderson. It didn't take long for Blazor to receive an official slot in the .NET ecosystem and by the time .NET 5 came out it was all but an official piece of the .NET pie. Ironically, that was also the era when Blazor stopped working on Internet Explorer and older versions of Microsoft Edge, which, looking back at the history of these attempts, is almost a scandalous prospect to contemplate.
|
||||
|
||||
Blazor leverages a new browser technology known as WebAssembly that, in short, attempts to allow a new interface to develop software in the web that runs in the browser. It is not a drop-in replacement for JavaScript but seems like it was designed to allow offloading some heavier calculations to a faster engine and code than what the browser engine currently allows. JavaScript is fast. Blazing fast since the inception of the Chrome V8 engine (and further developments since) then but the age of the engines and the language is starting to show. Yet, the web being the universal interface and backward compatibility is a hard requirement, rewriting everything from scratch and deprecating decades of history would be a suicide as far as technology adoption is concerned. Hence, WebAssembly.
|
||||
|
||||
What blazor does, from simply the developer's perspective, is it allows you to write your code in Razor and C# and the code is then compiled into WebAssembly. You are, in essence, running an entire .NET framework in your browser. An interop JavaScript exists to translate .NET code into JavaScript and vice versa. WebAssembly does not have access to DOM, the primary means with which to affect the web page and thus, this "glue" is required to make anything possible.
|
||||
|
||||
Blazor comes in two flavors: server side (known as blazor server or simply blazor) and client side (blazorwasm). The difference between the two is as follows.
|
||||
|
||||
Blazor server runs on the server (duh). Each interaction with the page from the user is sent to the server. That means your clicks, selects, text inputs etc. need a roundtrip to the server before being processed and response displayed on the page. Websocket, which is a browser's real-time two-way communication platform, serves as the go-between of these interactions so the experience is still app like.
|
||||
|
||||
Blazorwasm on the other hand, acts more like Angular or React that the code managing the web page all live in the browser.
|
||||
|
||||
And with this difference, we come to my first annoyance with the technology.
|
||||
|
||||
## The Subtle Differences Between Wasm And Server Can Bite You In The Ass
|
||||
Blazor comes off as a singular unified concept and it actually pulls it off as far as developer experience goes. Mostly. There are however differences that will infuriate you if you don't know it beforehand. Here's what I stumbled on.
|
||||
|
||||
My experience with Blazor began through wasm. I felt like running every click and type to the server would be an unacceptable delay and dependent way too much on an active and robust internet connection. However, as I began to write an app that was meant to run in the local machine alone (meaning served locally on the machine as well) I felt Server was worth looking into since any network delay would be negligible. Imagine then my shock when I leveraged my wasm knowledge and when it was time to click, nothing happened. No events were fired. No interactivity of any kind. The page showed content alright but when it was time to submit forms or handle click events, nada.
|
||||
|
||||
My naivete in the uniformity of Blazor became my small undoing. In retrospect, I suppose I should have treated Blazor Server and Blazor Wasm something like AngularJS and Angular but hey they *seem* to work the same in every other respect.
|
||||
|
||||
You see, as it turns out, Blazor **Server** has a unique trait called **Render Mode**. What is Render Mode? Well, as I see it, it instructs Blazor to optimize itself. If you only present information and have no interactivity, you really don't need all that boilerplate and plumbing to handle all those clicks that never materialize. Hence, Blazor acts like a static website instead. Plain content. Render Mode needs to be in `InteractiveServer` mode for that to work.
|
||||
|
||||
And it is **not** ON by default.
|
||||
|
||||
I know. I know. RTFM, right? Cut me some slack, please. If everything else works almost as universally between the two variants, is it reasonable to even assume this kind of little tidbit to be necessary for one to work and not the other? I say no.
|
||||
|
||||
It confuses me as to the justification for a setting like Render Mode and why is it how it is by default. One would imagine the primary purpose of Blazor to be the desire to create a single-page application experience and real time app. Why would you then disable by default all the code necessary to make that interactivity happen? I do not understand. After all, if you want to create just a static page, create a bloody static site. We have the technology. Personally, I'd have gone the opposite route: enable interactivity by default and for those who wanted to finetune case-by-case basis, allow this mode to be tweaked.
|
||||
|
||||
And by the way, there is no indication of any kind that this mode is the culprit. No log messages or error messages or warnings that interactive mode is X. I went down the rabbit hole trying to unravel why my components were disposed immediately after init thinking there was a bug in the framework itself before realizing this snafu.
|
||||
|
||||
Anyway, now I know you need this mode. And now, so do you. I hope this saves you a couple of hours of frustration, debugging and grey hairs. The good thing is, you only need to do it once in `App.razor` and the effect trickle's down like a booming capitalist economy. Unless you really want to do that fine-tuning per page or something.
|
||||
|
||||
This is what it takes by default to make all your pages and components interactive:
|
||||
|
||||
```html
|
||||
<body>
|
||||
<Routes @rendermode="InteractiveServer" />
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
</body>
|
||||
```
|
||||
|
||||
Let's look at another feature next that on paper, seems like a wonderful thing, but in practice, could use some more polish.
|
||||
|
||||
## CSS Isolation Works... Until It Doesn't
|
||||
Blazor has this feature that is pretty ingenious as far as css isolation is concerned. Here's how it works.
|
||||
|
||||
If you create a component in Blazor and then create an accompanying css file, the build process does something interesting. It adds to the html an attribute and a matching css selector so any css you write in the component css file is scoped to that component alone. Like so:
|
||||
|
||||

|
||||
So here's what the razor file would look like. Just a basic component. Then let's look at the css.
|
||||
|
||||

|
||||
Notice the lack of specific selectors. Just *button*. And the result would, as expected, look like this:
|
||||
|
||||

|
||||
One might assume based on that code that every button would be red, right? Not so. Here's where Blazor's magic comes in. Let's look at the generated html.
|
||||
|
||||

|
||||
Notice the attributes? The build process has added these and made them unique throughout the html tree depending on the component (or file) the code is in. `Main`, `div` and `article` elements are different from `h1`, `p` and `button` because the first three are in *MainLayout.razor* file while the latter three are in *Counter.razor*. Looking at the generated css:
|
||||
|
||||

|
||||
We discover the same attribute as a selector making it as precise (or more so) as an id attribute to ensure only the specific component buttons are red.
|
||||
|
||||
The ability to use attributes as a css selectors have been in the spec and supported by browsers for a while but Blazor might be the first time I have seen them used like this. Usually attribute selectors have been used to target specific input fields via name or type like so `input[type="text"]` but the spec seems to allow for just the attribute as a selector as well making it a cool way to create self-contained, isolated components without the need to add classes or ids to every piece of component built.
|
||||
|
||||
However, not all is sunshine and roses with this feature. There are, unfortunately, exceptions. And the most glaring of those is `EditForm`.
|
||||
|
||||
If you were to naively write code like this:
|
||||
|
||||
```html
|
||||
<EditForm Model="FormData">
|
||||
<InputText @bind-Value="FormData.Name" />
|
||||
</EditForm>
|
||||
```
|
||||
```css
|
||||
form {
|
||||
background: red;
|
||||
}
|
||||
input {
|
||||
background: blue;
|
||||
}
|
||||
```
|
||||
You would be surprised to find that the form is **not** red and the input field is **not** blue. What's with that? For some reason or other, `EditForm` does not respect the same isolation rules. In fact, `EditForm` does not have isolation rules it seems. The generated html does not have custom attributes inside the generated `<form>`, while the css still does have the selector.
|
||||
|
||||
In order for css isolation to function within such an environment, you need some extra magic for both the razor code and css.
|
||||
|
||||
```html
|
||||
<div>
|
||||
<EditForm Model="FormData">
|
||||
<InputText @bind-Value="FormData.Name" />
|
||||
</EditForm>
|
||||
</div>
|
||||
```
|
||||
|
||||
```css
|
||||
::deep form {
|
||||
background: red;
|
||||
}
|
||||
|
||||
::deep input {
|
||||
background: blue;
|
||||
}
|
||||
```
|
||||
|
||||
Let's break it down. Since `<EditForm>` won't receive the isolation attribute, we need something that does. Hence the `div`. But that is not enough because the css previously still targeted the actual form and input fields. So we use `::deep`, which switches the selector scope so the attribute selector is a parent selector like so:
|
||||
|
||||
```css
|
||||
[b-jarguqllqi] form {
|
||||
background: red;
|
||||
}
|
||||
|
||||
[b-jarguqllqi] input {
|
||||
background: blue;
|
||||
}
|
||||
```
|
||||
|
||||
Personally, I dislike this approach because it adds an unnecessary extra layer to the generated html but that seems the only way to make isolation work.
|
||||
|
||||
Next one is not a big deal but it is one to keep in mind when developing Blazor applications.
|
||||
|
||||
## Deviating From Expected File Structure Has Consequences
|
||||
The basic structure of a Blazor app is that you've got a single folder `Components` within which you have all the code. However, some developers might want to organize their files in a different method by grouping files together by theme rather than by type. This is known as a feature model or vertical slice. So instead of having separate folder for Models, Controllers, Views etc. you have FeatureA and FeatureB and within each feature folder you have the associated model files, controller files, views, components, services etc.
|
||||
|
||||
Should you attempt to use this method in Blazor, expect to do some extra work. Mainly, you might need to import extra libraries into the feature folder itself. For example, to do interaction with buttons and the like, you need to import `@using Microsoft.AspNetCore.Components.Web`. Despite the fact that there is already an `_Imports.razor` file, you still need to do this if you deviate from the Blazor's default structure. There are two ways to import these libraries into your component.
|
||||
|
||||
1. Import directly into the component
|
||||
2. Create a `_Imports.razor` file at your feature folder
|
||||
|
||||
Finally, let's discuss very briefly some error management.
|
||||
|
||||
## Stacktraces Are Useless
|
||||
I have observed that stack traces on the frontend seem to be mostly useless. Sure they still work on the backend but if you try to parse any stack trace you see on the browser console, you are fucked. The abstraction layers are so thick and deep that it is impossible to trace where in your code the problem is. Instead, you are relegated to the oldest of old-school debugging: removing code until things work and then adding it, veeeery carefully, back in again. It doesn't help that hot-reload logic is inconsistent and unreliable at times depending on the kind of code you write so there might be stacktraces for reasons outside of your control that have nothing to do with the feature you are writing.
|
||||
|
||||
## Conclusion
|
||||
Blazor on paper seems like a wonderful technology especially if your bread and butter comes mainly from .NET ecosystem. Being able to code everything in a single language is a dream many a developer have tried to implement. Context switching is a real pain and breaks often the flow of a developer's mind. NodeJS began as a dream like this and may be currently the most successful attempt to use a single language to handle the full stack of software development journey from UI to server to database (see MEAN stack for more on that) and that language is JavaScript.
|
||||
|
||||
Blazor comes close. Really close. But would I recommend this for anything but a hobbyist project and perhaps some internal company pet project? No. If I needed to write mission-critical software tomorrow from scratch, I would still opt for the UI to be React, Angular or any other of that ilk. I would use .NET in the backend though. 100%. The overall inconsistency in the developer experience for Blazor is just too much. Yes, now that I know these pain points, I can compensate for them but would I suffer another developer to face the same frustrations and pains in order to deliver value? I would not.
|
||||
|
||||
As an aside the talent pool in the wild still heavily favors JavaScript and its various frameworks as well, making Blazor a hard sell as an investment. Granted, it is a catch-22. There won't be experienced developers for the technology if no one uses the technology but for me to seriously begin to contemplate recommending Blazor to my customers or colleagues, I need to wait a little while more. See if some of these pain points of mine are smoothed over and rectified.
|
||||
|
||||
Time will tell if Blazor becomes another Silverlight or a viable alternative to plain old html/css/js stack. It is entirely possible, that another language might take a bite at this apple as well. Some already have. Rust for example can be compiled to webassembly though I know not if it has similar interop as Blazor does for DOM manipulation or how robust it is. All of this also relies on the success of WebAssembly as a technology. If that sizzles and is abandoned, these frameworks and libraries will follow suit. JavaScript does seem to be a king with no equal since it has endured while others have fallen to obscurity and were history to repeat itself, it paints no flattering picture for Blazor or its contemporaries.
|
|
@ -1,8 +0,0 @@
|
|||
Created: 2024-05-01 10:20:00
|
||||
Updated: 2024-05-01 10:20:00
|
||||
Title: Reflecting On 10 Years As a Developer
|
||||
Slug: reflecting-on-10-years-as-a-developer
|
||||
Description: Some thoughts and musings about my journey for the past 10 years as a developer
|
||||
Tags:
|
||||
- rants
|
||||
- miscalleanous
|
|
@ -1,57 +0,0 @@
|
|||
## Intro
|
||||
It has been roughly ten years since some poor soul decided it was a good idea to pay me to do this thing called software. Since this industry follows the Jem'Hadar rules or aging, this little milestone supposedly makes me an elder. So as a senior citizen of the IT world, why not dole out some thoughts for everyone to ignore at their leisure? Let's yell at some clouds, share some wisdom and put some pen to the paper.
|
||||
|
||||
So here's my five "lessons for life in software".
|
||||
|
||||
## 1. There is no work-life balance
|
||||
If you feel like you need to balance how much you work and how much you live, I fear you have already lost. To your work. So *waves a hand all jedi like* put aside that death stick, go home and rethink your life. Work is there to facilitate your life. Not the other way around.
|
||||
|
||||
Work is there so you can live. You don't live so you can work.
|
||||
|
||||
One more time.
|
||||
|
||||
Work is not life. Life is not about working.
|
||||
|
||||
Work to live. Don't live to work.
|
||||
|
||||
## 2. Best practices are like opinions
|
||||
And like opinions, they are like assholes: everyone's got one and sooner or later it gets smelly.
|
||||
|
||||
The best way to treat any blog post, book or declaration of best practices is to view them as a trend. Trends come and go. Treat best practices not as scripture but a nice offer of principles that might, **might**, work for you and your team. If it does, good for you. If it doesn't, well you'll form your own eventually and they are as valid as the guru you just read from.
|
||||
|
||||
Let me put it this way: You read a blog post about best practices (as one does). Then a year later you read another that says absolutely opposite of what you read a year previous. It might be even from the same author! Are you going to refactor your entire code base to follow this new paradigm because your favorite guru told you so? Hell no (I hope that's your answer).
|
||||
But they are all united in the desire to make work and life easier.
|
||||
So what's a code monkey to do? One word: consistency. That's really what best practices in the end are. Consistent way of doing things. Whatever rules you've got set up, stick to them.
|
||||
|
||||
## 3. Fancy titles look like imposter syndrome
|
||||
I am a software developer. I am not a "software artisan", "code ninja", "devops master super engineering supreme commander" or my personal pet peeve, a "software engineer".
|
||||
|
||||
Maybe I'm in the minority but I am predisposed to be suspicious of all things hype because usually if it sounds too good to be true, it is. The fancier and more pretentious your title sounds, more eagerly I start looking around for the curtain to see what's really going on and on whose face do I have to throw some water.
|
||||
|
||||
Call me humble and maybe I am in the minority but it feels arrogant and presumptuous to invent some fancy name to describe what I do when what I do is not really that different or special than what others do. If you feel like you need to invent a new word to describe how awesome you are, odds are you really are not, but are trying to pretend you are. That's called lying by the way.
|
||||
|
||||
Now, titles that describe a **function** of your work like a tester, devops, database admin and so forth are fair game because they serve to inform rather than impress.
|
||||
|
||||
As the Bard put it: "This above all: to thine own self be true."
|
||||
|
||||
## 4. And I too feel like one on occasion
|
||||
The imposter syndrome. Simply put, an idea that you are really not that good at your job and somehow against all odds you have been faking it this entire time. Your boss, your coworkers, your clients. Everyone somehow bought into the hype that is **you** and chose to give you money for absolutely nothing. Your entire career is a con artists wettest dream come true.
|
||||
|
||||
Sound pretentious? Welcome to the paradox friend.
|
||||
|
||||
It has been my observation that the panacea for this particular illness is not a pill but two things: time and experience. The more you do, the more you learn and what skills you have get honed further. That doesn't mean you don't get that feeling still on occasion. But you are better equipped at managing it and putting it aside. Because turns out, you are qualified.
|
||||
|
||||
|
||||
## 5. Curiosity killed the cat. Good thing you are not a cat.
|
||||
Software is an industry that moves at lightning speed. Almost every branch of this industry constantly changes and evolves. Tools change, new ways are invented to solve problems, abstractions are abound. Sometimes they are better, sometimes they are not.
|
||||
|
||||
Imagine if you drove a taxi for a living and every few months you get a new car but the pedals are all different, the shift gear has switched position to the roof and instead of sitting down you are standing up. Or next time you are sitting down but the shift gear is automated. Or let's say you are a plumber and every few months you need to be certified for a new kind of a wrench that But they are all united in the desire to make work and life easier.promises to double your torque.
|
||||
|
||||
This is the reality IT and software as an industry is living in. Of course other industries change and evolve as well but in the software world that evolution cycle is much much faster and if you don't watch it, you might get left behind. So if one is to survive, the most important trait in any developer's toolkit is curiosity. The desire to learn new things and at least keep track of industry happenings. Yes that might mean spending a day or two to learn another JavaScript framework (sigh), database or a new fancy code offering but that's the nature of the beast today.
|
||||
|
||||
That is unless you are a Cobol developer working for banks. Those skills live forever and you are set for life. Unless a Rust developer comes along...
|
||||
|
||||
Anyway, dare to be curious. It will be rewarded.
|
||||
|
||||
## Outro
|
||||
So there's some "rules" that I try my best to apply to myself. You might even call them my "best practices" for living and working. Some of them are just observations of phenomenon and how to overcome them. Other's at times even harsh lines that I try not to cross. I hope you find this little post informative, though provoking, and perhaps even entertaining.
|
|
@ -1,28 +0,0 @@
|
|||
## Intro
|
||||
The internet is a wonderful invention. I am biased of course because without the internet, I wouldn't have a job. Still, the interconnectivity of the entire world and the ability to communicate (sometimes very complex things) practically instantaneously to anyone and everyone is something that changed the world. However, like all things in life and the universe in general, there is a different side to this coin. And the reason for that uglier side lies in the very nature of humankind.
|
||||
|
||||
## I still remember
|
||||
I was born in the early 90's. This makes me among the last to live without the internet. Millennials in general are going to be the last generation to remember what it was like. How, if you wanted information, you opened a physical book. Or you went to the library and browsed the shelves. Or you asked someone. How the greatest achievement in communication was a long-distance phone call and maybe a text message.
|
||||
|
||||
Travel was still around but opportunities to network and meet other people, let alone the kind that shared your views of the world, were not as ordinary as today. The world was smaller and comprised of smaller communities. And because they were small, so was the sphere of influence.
|
||||
|
||||
There was also no memory that lasted beyond a few years. Youthful indiscretions could not haunt you till the end of your days and mistakes of the past were forgotten. And if the local community vilified you for them, you could move to a different one and start fresh. Now, every act, every mistake and every thought you've ever had, past and present, are up for grabs anytime. Anywhere. Your mistakes will haunt you forever and follow you everywhere. And the value by which you are condemned, changes constantly.
|
||||
|
||||
## Let come wrath, let come ruin
|
||||
Looking back, humankind may have been too young for the internet. The power that lies with it. There is unfortunately an aspect to our nature that sadly is enhanced rather than subdued by the internet and that is tribalism. The desire and proclivity to gather together under a shared dream, an idea, a cause. In the abstract, that sounds wonderful. Civilization as it stands would not exist if it wasn't for that trait. But there is a dark side. With tribalism comes doctrine. And with doctrine comes conformity. And to conform is to relinquish all individuality for the sake of the group, the cause, the idea. And in no action is this behavior more prevalent than in condemnation and judgment we pass over one another. Not for justice. Not for truth. But because the cause and the tribe demands it.
|
||||
|
||||
It is, I would argue, a foundational concept of the civilized, western society that should a person stand accused of impropriety, they shall be considered innocent until proven guilty. Now some will argue this strictest definition will only apply to crimes and courtrooms. To them, I say they are dead wrong. For laws begin as social norms. Codified and formalized but the origins remain in the minds, actions and attitudes of the people. And those trying to argue what they would probably see as "nuance" are arguing a straw man and only reveal the ugliness of their lack of morality, honor or basic human decency. An excuse not to put their money where their mouth is and walk the talk. Harsh words? Perhaps. But invalid? No. After all, how can you believe in the concept of justice if you yourself do not live by it? If you don't, in your everyday life, attempt to live by the creed your society and culture are founded on, what are you even doing? Well, what you are doing is being a parasite: receiving benefits while giving nothing of substance in return. That or your standards of evidence are criminally low as far as establishing guilt for anything.
|
||||
|
||||
There is an inherent allure to mob mentality and condemnation that hits the right boxes as far as the human psyche is concerned. Hence why it is so popular. Within a mob, individual accountability disappears while giving you every opportunity to reap the benefits with little to no risk. The shared delusion of the mob allows you the surety and certainty of a conviction without the need for evidence or proof of transgressions. With the power of the internet, the mob is now beyond counting and that power equally as intoxicating. A supersized confirmation bias in action. When seemingly everyone around you passes judgment, it is really hard to stop and ask for evidence. After all, your neighbor is so certain and they are such a decent person, how could they be wrong? Calling into question the judgment is to call **them** into question and you don't want to suggest your friend is a liar, right?
|
||||
|
||||
That's the fallacy in play that allows you to justify condemnation with little to no evidence of actual wrongdoing. Certainty of the group combined with an unwillingness to ask for facts due to the inconvenience of emotion and the mob's inherent nature to protect itself by turning against naysayers. And to add insult to injury, there is another sad aspect to all this. To pass judgment like that feels good. Not only does one get to see someone (oftentimes a person disliked for one reason or another) hurt as a surrogate for a person's own pain, but also get to feel moral superiority. "I have brought down evil!" "I am moral and righteous in my fury of this person!" "I am a god!". That last part very few actually think (I hope) but the sentiment is there. To be part of a mob is to wield power beyond your own capabilities. Power over who lives and who dies. Sometimes literally for mob justice tends to take no prisoners. What does that make you but a god?
|
||||
|
||||
And thus an individual, with very little power alone, gets to participate in a power trip (and with the internet that trip reaches immense proportions). They can feel pride over their moral superiority. Engorge themselves in the gluttony of righteous fury. Administer "justice" by delivering wrath upon the party they deem guilty. And how often do those who participate in these mobs and are proven wrong are called to account? Never. They are either implicitly forgiven or the matter is conveniently forgotten but no shame is brought to those who participate in these mobs. And they will not admit to their sin. Their presumption. Because it is so damn easy just to move on to the next emotional thing.
|
||||
|
||||
## The repercussions are there
|
||||
One might notice I used a few very specific words. Those who have read your Bible certainly recognized a few choice words. Pride. Gluttony. Wrath. These are counted among the seven deadly sins: acts that land you in hell once for you the bell shall toll. Arguments could be made that, depending on your reasons (conscious or unconscious) for participating in mob justice, other sins could be added to the list. Envy. Greed. Perhaps even Sloth. That's a lot of baggage from one act. Righteous though it may feel at the time. There's the key word: feel. Too often than not, we allow emotion to override our better judgment. We allow our desire for vengeance to paint the world we see and such desires demand an outlet. So we jump to the chance when an impropriety is revealed to vent our frustrations and rage. Usually, the source of that frustration and rage is a perceived injustice elsewhere but to rectify that one is so hard and difficult it is easier to reach for the low-hanging fruit that delivers the same emotional gratification as the other one with a fraction of the effort needed to accomplish the task.
|
||||
|
||||
## Final thoughts
|
||||
So few people it seems have the fortitude, the grace and the strength to stand by the principles they claim to live by. When the mob comes at your door, more often than not we give up and capitulate to their demands. In the name of survival, we abandon the very principles that we supposedly are ready to die for. We know it's power and are loathe to put ourselves in the crosshairs.
|
||||
|
||||
So as a parting wisdom, I offer this: more often than not, you are not in possession of all the facts and as such, wait just a little while, until passing judgment or participating in a mob or mass cancellation of someone. Take a moment to ask yourself why are you doing this. Is it truly justice you serve or are you appeasing your own emotional need? Are you passing judgment on the person by facts or because you just don't like the guy? And most importantly, ponder on this: would you yourself like to be judged and condemned by that same criteria and information presented to you right now? If your answer is yes, well, I suppose you have made your bed. But if the answer is no, perhaps you will find in your heart the courage to decline the invitation to participate. As alluring as it is, your soul might find itself marred by such action far more permanently than you think.
|
|
@ -1,8 +0,0 @@
|
|||
Created: 2024-07-04 22:10:00
|
||||
Updated: 2024-07-04 22:10:00
|
||||
Title: The Frustrating Lack Of Principles And Mob Mentality Of The Internet
|
||||
Slug: lack-of-principles-and-the-mob-mentality
|
||||
Description: The internet is a wonderful invention but one I fear humanity was not ready for and has in fact brought out the worst impulses of mankind rather than it's best.
|
||||
Tags:
|
||||
- rants
|
||||
- miscalleanous
|
Binary file not shown.
Before Width: | Height: | Size: 153 KiB |
|
@ -1,14 +0,0 @@
|
|||
Created: 2024-04-29 17:00:00
|
||||
Updated: 2024-04-29 17:00:00
|
||||
Title: Bluetooth 5.1 Headset In Dual Boot Environment
|
||||
Slug: bluetooth-5-1-headset-in-dual-boot-environment
|
||||
Description: A step by step tutorial on how to connect a bluetooth 5.1 headset to both Windows and Linux environment using Bose Quietcomfort Ultra a a case study,
|
||||
Tags:
|
||||
- tutorial
|
||||
- bluetooth
|
||||
- bluetooth 5.1
|
||||
- headset
|
||||
- bose quietcomfort ultra
|
||||
- dual boot
|
||||
- Windows
|
||||
- Linux
|
|
@ -1,228 +0,0 @@
|
|||
Bluetooth 5.1 Headset In Dual Boot Environment
|
||||
===============================================
|
||||
|
||||
## Intro
|
||||
In this tutorial we will attempt to allow the same bluetooth device (a Bose Quietcomfort Ultra headset to be precise) to be paired and connected to a dual-booted Windows/Linux system without needing to re-pair the headset every time you switch between operating systems. The process is cumbersome and requires many steps but it is not impossible.
|
||||
|
||||
## The Problem
|
||||
When pairing bluetooth devices to each other, those devices keep track of each other via a mac address: an unique number-character combination that identifier a particular device. Pretty much every device that is used to connect to something whether it's a set of headphones, network adapter, an usb stick or a game controller, has an unique mac address attached to it.
|
||||
|
||||
Imagine then that you pair a set of headphones to your computer. The computer now has a record of your headphones mac address and the headphones has a record of your computer's bluetooth adapter's mac address.
|
||||
|
||||
Mac addresses however, are only half the equation. The other half is how these devices authenticate with each other so your headphones know which computer is allowed to send it sound and which is not. This is done via keys that computer keeps in it's record and these keys are generated every time a pairing occurs.
|
||||
|
||||
So let's say that you pair your headphones to a Linux computer and then boot into a Windows operating system on the same computer and attempt the pairing again. It will work, but now
|
||||
the only operating system that has the correct key, provided by the headphones, is on Windows. Reboot to Linux and attempt to connect and you are met with an error since your Linux machine,
|
||||
despite having a record of the headphone mac address, now has the wrong key to authenticate and the headset has already cleared it's internal records for any authentication keys belonging to Linux. The simple rule of thumb is: one mac address, one set of keys.
|
||||
|
||||
## The solution briefly
|
||||
The solution to this conundrum is simple (how to make that solution happen, not so much): copy the keys from Windows into Linux. Now there are multiple projects out there that have attempted to fix this problem but they seem to work only for older devices. More specifically devices that operate with bluetooth 4 and and maybe 5. So we'll do this by hand for now.
|
||||
|
||||
## About Bluetooth 5.1
|
||||
Bluetooth had some changes to it as far as key management after version 5 or 5.1 (don't remember which). What happened was that things got more complicated (shocker that). In earlier versions of bluetooth, there was just one key to be concerned about. Now there are several. So if you do not find in your .reg file the necessary keys, there is a change that your device is of the older version. That means, you actually might get off this exercise slightly easier than I did. I suggest looking to [Arch Wiki](https://wiki.archlinux.org/title/bluetooth#Dual_boot_pairing) or search for dual boot bluetooth device and you'll come across several tutorials that will go through it. The process is virtually the same, just much simpler.
|
||||
|
||||
I'll still suggest reading through the relevant wiki section for reference and potential extra help if these instructions prove insufficient.
|
||||
|
||||
## How to make it happen
|
||||
|
||||
Let's break this down into steps. We need to...
|
||||
|
||||
1. Pair and connect our device in Linux
|
||||
2. Pair and connect our device in Windows
|
||||
3. Extract the necessary registry keys from Windows
|
||||
4. Transfer (and transform) the keys to Linux
|
||||
5. Update Linux configuration so headset will accept new keys
|
||||
|
||||
Easy right? Weelll... the first couple of steps are.
|
||||
|
||||
### Phase 1: Pairing On Linux
|
||||
The first step is rather simple. We pair and connect our bluetooth headset with Linux.
|
||||
|
||||
### Phase 2: Bootin Into Windows And Pairing Again
|
||||
Step 2, not so difficult either. Just make sure the headphones are in pairing mode and once Windows pairs and connects, we are in business.
|
||||
|
||||
### Phase 3: Extracting Keys
|
||||
Here's where things get a little trickier. We need access to windows registry to extract our keys and we can't just launch regedit. This is due to the fact that we need extraelevated privileges, aka SYSTEM account, to view all the information we need. To do that, we use [pstools](https://learn.microsoft.com/en-us/sysinternals/downloads/pstools). What you do is this:
|
||||
|
||||
1. download pstools and extract to a folder (I use *C:\Programs\pstools*)
|
||||
2. open command prompt, powershell or windows terminal **as** administrator
|
||||
3. navigate to folder pstools
|
||||
4. run `.\PsExec.exe -i -s regedit`
|
||||
|
||||
We should see now windows registry editor open.
|
||||
|
||||
^^^
|
||||

|
||||
^^^ Windows registry when opened with PsExec
|
||||
|
||||
Navigate down the registry heys
|
||||
- HKEY_LOCAL_MACHINE
|
||||
-> SYSTEM
|
||||
-> CurrentControlSet
|
||||
-> Services
|
||||
-> BTHPORT
|
||||
-> Parameters
|
||||
-> Keys
|
||||
-> [BLUETOOTH ADAPTER MAC ADDRESS]
|
||||
|
||||
Beneath [BLUETOOTH ADAPTER MAC ADDRESS] there is mac addresses for each bluetooth device paired. Other guides as I recall will ask to export a specific device registry keys but what we will do is export the the entire [BLUETOOTH ADAPTER MAC ADDRESS] device. That should give us a *.reg* file that looks something like this:
|
||||
|
||||
```
|
||||
Windows Registry Editor Version 5.00
|
||||
|
||||
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\BTHPORT\Parameters\Keys\1cc10c325f27]
|
||||
"CentralIRK"=hex:25,02,6b,0e,c2,1a,f8,37,8c,c3,d0,d2,7a,f9,4a,6e
|
||||
"acbf71cef184"=hex:d8,97,cd,2f,7b,6c,ab,e2,12,62,c1,db,46,e5,b4,fe
|
||||
|
||||
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\BTHPORT\Parameters\Keys\1cc10c325f27\acbf71cef184]
|
||||
"LTK"=hex:ef,c1,03,0c,cb,d1,90,f1,61,14,b2,1a,52,45,11,ba
|
||||
"KeyLength"=dword:00000010
|
||||
"ERand"=hex(b):00,00,00,00,00,00,00,00
|
||||
"EDIV"=dword:00000000
|
||||
"IRK"=hex:1b,5a,69,12,cc,06,0b,23,91,ab,6f,e8,8c,fe,ba,7e
|
||||
"Address"=hex(b):84,f1,ce,71,bf,ac,00,00
|
||||
"AddressType"=dword:00000000
|
||||
"CEntralIRKStatus"=dword:00000001
|
||||
"AuthReq"=dword:00000020
|
||||
```
|
||||
|
||||
We are interested in three lines:
|
||||
1. "acbf71cef184"=hex:
|
||||
2. "LTK"=hex: key
|
||||
3. "IRK"=hex: key
|
||||
|
||||
We copy these three lines or the entire file to an usb stick or something else that can be accessed via Linux.
|
||||
|
||||
We are done with Windwows so let's reboot back into Linux.
|
||||
|
||||
### Phase 4: Transforming keys
|
||||
The keys from Windows are not applicable as themselves. We need to do some modifying to them. Don't worry. It's not too difficult. What we must do is:
|
||||
|
||||
1. Remove commas
|
||||
2. Turn the entire string to uppercase
|
||||
|
||||
```
|
||||
"acbf71cef184"=hex:d8,97,cd,2f,7b,6c,ab,e2,12,62,c1,db,46,e5,b4,fe
|
||||
becomes => D897CD2F7B6CABE21262C1DB46E5B4FE
|
||||
|
||||
"LTK"=hex:ef,c1,03,0c,cb,d1,90,f1,61,14,b2,1a,52,45,11,ba
|
||||
becomes => EFC1030CCBD190F16114B21A524511BA
|
||||
|
||||
"IRK"=hex:1b,5a,69,12,cc,06,0b,23,91,ab,6f,e8,8c,fe,ba,7e
|
||||
becomes => 1B5A6912CC060B2391AB6FE88CFEBA7E
|
||||
```
|
||||
|
||||
### Phase 5: Updating Linux configuration
|
||||
|
||||
It is recommended to stop bluetooth service at this point.
|
||||
|
||||
```
|
||||
systemctl stop bluetooth
|
||||
```
|
||||
|
||||
Then we navigate to the *[BLUETOOTH ADAPTER MAC ADDRESS]* folder in our Linux bluetooth configuration.
|
||||
|
||||
```
|
||||
cd /var/lib/bluetooth/[BLUETOOTH ADAPTER MAC ADDRESS]
|
||||
```
|
||||
|
||||
Inside we will find folders for each bluetooth device similarly like in Windows registry. We make a copy of it as a backup
|
||||
|
||||
```
|
||||
cp -r [DEVICE MAC ADDRESS] [DEVICE MAC ADDRESS].orig
|
||||
```
|
||||
|
||||
And then modify the `info` file inside *[DEVICE MAC ADDRESS]* folder. It should look something like this.
|
||||
|
||||
```
|
||||
[General]
|
||||
Name=Bose QC Ultra Headphones
|
||||
Class=0x2c0404
|
||||
AddressType=static
|
||||
SupportedTechnologies=BR/EDR;LE;
|
||||
Trusted=true
|
||||
Blocked=false
|
||||
Services=...
|
||||
|
||||
[DeviceID]
|
||||
Source=1
|
||||
Vendor=158
|
||||
Product=16486
|
||||
Version=340
|
||||
|
||||
[LinkKey]
|
||||
Key=[KEY FROM PHASE 1]
|
||||
Type=7
|
||||
PINLength=0
|
||||
|
||||
[IdentityResolvingKey]
|
||||
Key=[KEY FROM PHASE 1]
|
||||
|
||||
[PeripheralLongTermKey]
|
||||
Key=[KEY FROM PHASE 1]
|
||||
Authenticated=2
|
||||
EncSize=16
|
||||
EDiv=0
|
||||
Rand=0
|
||||
|
||||
[SlaveLongTermKey]
|
||||
Key=[KEY FROM PHASE 1]
|
||||
Authenticated=2
|
||||
EncSize=16
|
||||
EDiv=0
|
||||
Rand=0
|
||||
```
|
||||
|
||||
Now we need to replace some keys with the one's modified a moment ago and here's how to use them:
|
||||
- The device key (D897CD2F7B6CABE21262C1DB46E5B4FE) goes to *[LinkKey]* section
|
||||
- IRK (1B5A6912CC060B2391AB6FE88CFEBA7E) goes to *[IdentityResolvingKey]* section
|
||||
- LTK (EFC1030CCBD190F16114B21A524511BA) goes to *[PeripheralLongTermKey]* and *[SlaveLongTermKey]* sections
|
||||
|
||||
Like so:
|
||||
|
||||
```
|
||||
[General]
|
||||
Name=Bose QC Ultra Headphones
|
||||
Class=0x2c0404
|
||||
AddressType=static
|
||||
SupportedTechnologies=BR/EDR;LE;
|
||||
Trusted=true
|
||||
Blocked=false
|
||||
Services=...
|
||||
|
||||
[DeviceID]
|
||||
Source=1
|
||||
Vendor=158
|
||||
Product=16486
|
||||
Version=340
|
||||
|
||||
[LinkKey]
|
||||
Key=D897CD2F7B6CABE21262C1DB46E5A4AF
|
||||
Type=7
|
||||
PINLength=0
|
||||
|
||||
[IdentityResolvingKey]
|
||||
Key=1B5A6912CC060B2391AB6FE88CDADC7E
|
||||
|
||||
[PeripheralLongTermKey]
|
||||
Key=EFC1030CCBD190F16114B21A527424BA
|
||||
Authenticated=2
|
||||
EncSize=16
|
||||
EDiv=0
|
||||
Rand=0
|
||||
|
||||
[SlaveLongTermKey]
|
||||
Key=EFC1030CCBD190F16114B21A527424BA
|
||||
Authenticated=2
|
||||
EncSize=16
|
||||
EDiv=0
|
||||
Rand=0
|
||||
```
|
||||
|
||||
We save the file and restart bluetooth service.
|
||||
|
||||
```
|
||||
systemctl start bluetooth
|
||||
```
|
||||
|
||||
Now the headphones should work both on Windows and Linux with no worries.
|
Loading…
Reference in a new issue