{
    "version": "https://jsonfeed.org/version/1",
    "title": "Oskar Okuno Blog",
    "home_page_url": "https://okuno.se/blog/",
    "feed_url": "https://okuno.se/blog/json",
    "description": "Oskar Okuno is a Fullstack Dev experienced with React, React Native, Vue. As a Fullstack Dev he also uses Node.js, Go, Java and C#.",
    "author": {
        "name": "Oskar Okuno",
        "url": "https://okuno.se"
    },
    "items": [
        {
            "id": "https://okuno.se/blog/find-which-chromium-version-to-commit",
            "content_html": "<p>NOTE: This article assumes you are using a M-Series MacBook which introduces the issue of running x86/amd software on an ARM based CPU.</p>\n<p>When you want to run an old Chromium version on new hardware/OS you may run into some obsticles. First, you need to recreate the environment that this ancient code expects. Then you need to download the source code of Chromium and build it yourself. However, it's a pretty tidus and time consuming process, which is very well documented <a href=\"https://chromium.googlesource.com/chromium/src.git/+/HEAD/docs/building_old_revisions.md\" rel=\"noopener noreferrer\">here</a>.</p>\n<p>A slightly faster way is to download a snapshot from the official: <a href=\"https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html\" rel=\"noopener noreferrer\">chromium-browser-snapshots</a>. This will skip the build step and cut the amount of enviornment setup needed. However, they are sorted by a mysterious number...</p>\n<h2 id=\"snapshot--revision\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#snapshot--revision\"></a>Snapshot = Revision</h2><p>The \"snapshot number\" is actually the <strong>revision</strong> number. So the challenge becomes: <em>How can I find out the revision number of the specific chrome version I am looking for?</em> This requires you to go through the source code where the major version corresponds to a tag which can lead you to a commit, which message has the revision number! Let the treasure hunt begin!</p>\n<h2 id=\"case-study-find-v36\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#case-study-find-v36\"></a>Case Study: Find v36</h2><p>From the <a href=\"https://chromium.googlesource.com/chromium/src/\" rel=\"noopener noreferrer\">source code</a> find the <a href=\"https://chromium.googlesource.com/chromium/src/+refs\" rel=\"noopener noreferrer\">Tag</a> that corresponds to the version you are looking for. There are usually a lot of tags for each MAJOR version. Click on one of the tags to see the source code tree at that point in time.</p>\n<p><img src=\"img/chrome-version-blame.jpg\" alt=\"Chrome Version Blame\" width=\"561\" height=\"360\" /></p>\n<p>Then you navigate to <code>src/chrome/VERSION</code>. This file shows you the version of Chrome at this point in time. Now, click \"blame\" and find the last commit that changed the major version. For this case it was this commit: <a href=\"https://chromium.googlesource.com/chromium/src/+/b1f8bdb570beade2a212e69bee1ea7340d80838e\" rel=\"noopener noreferrer\">b1f8bdb570beade2a212e69bee1ea7340d80838e</a></p>\n<p>The commit message is where we will find the revision number:</p>\n<pre><code>36 is the atomic number of krypton.\n36 is both the square of 6 and a triangular number...\n\n[...]\n\nR=dharani@google.com, laforge@chromium.org\n\nReview URL: https://codereview.chromium.org/218183002\n\ngit-svn-id: svn://svn.chromium.org/chrome/trunk/src@260368 0039d316-1c4b-4281-b951-d872f2087c98</code></pre><p>What we are looking for is on the last line after <em>...trunk/src@</em>: <code>260368</code>. This is the revision number! It seems like there is no strict format when it comes to specifying the revision number in the commit so it may look different for different commits. Now we can go to the snapshot folder of the specific architecture we are looking for (e.g. Linux_x64) and search for the revision number and then download the zip file with the chrome binary.</p>\n<p>See source code <a href=\"https://github.com/MrOggy85/old-chrome-mac\" rel=\"noopener noreferrer\">here</a></p>\n<h2 id=\"resources\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#resources\"></a>Resources</h2><ul>\n<li><a href=\"https://stackoverflow.com/questions/61274601/how-to-find-out-in-which-chromium-version-a-commit-was-shipped\" rel=\"noopener noreferrer\">https://stackoverflow.com/questions/61274601/how-to-find-out-in-which-chromium-version-a-commit-was-shipped</a></li>\n<li><a href=\"https://medium.com/dot-debug/running-chrome-in-a-docker-container-a55e7f4da4a8\" rel=\"noopener noreferrer\">https://medium.com/dot-debug/running-chrome-in-a-docker-container-a55e7f4da4a8</a></li>\n</ul>\n",
            "url": "https://okuno.se/blog/find-which-chromium-version-to-commit",
            "title": "How to find Chromium Snapshot by Version",
            "summary": "When you want to run an old verson of Chrome",
            "image": "https://okuno.se/blog/img/chrome-docker-old.jpg",
            "date_modified": "2025-05-26T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/what-does-gravity-actually-look-like",
            "content_html": "<p>You know how gravity is usually explained with that downwards funnel? A 2D grid, then someone plops a big bowling ball on it, and suddenly it's all marbles spinning into it. That analogy gets shown in every pop-science explainer ever — and for years, it totally threw me off.</p>\n<p>So I dug in. Here's what finally made gravity click for me.</p>\n<h2 id=\"the-funnel-is-lying-to-you-sort-of\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#the-funnel-is-lying-to-you-sort-of\"></a>The Funnel Is Lying to You (Sort of)</h2><p>The Funnel analogy tries to explain how a massive object like the Sun \"bends\" space so that smaller objects, like planets, move around it. But there are problems. It uses gravity (pulling down into the funnel) to explain gravity, which is a bit of a loop. Furthermore, the funnel is 2D. The Sun is a 3D ball. So why does the ball sink downward? Why not toward the Sun itself? And Earth isn't spiraling into the Sun like a marble down a funnel, so what gives?</p>\n<p>The analogy is simple, but also misleading. It shows some effects, but hides the real structure underneath.</p>\n<h2 id=\"space-isnt-nothing-its-a-stage-with-rules\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#space-isnt-nothing-its-a-stage-with-rules\"></a>Space Isn't Nothing. It's a Stage with Rules.</h2><p>We think of space as \"nothing,\" but it''s actually something — a structure with properties. It has distances, directions, and time. And it has geometry — meaning it can be curved, warped, or stretched.</p>\n<p>Even in a total vacuum, light still travels at a constant speed, clocks still tick, and you can measure how far something is. So this \"nothing\" has a shape — and that shape is affected by mass and energy.</p>\n<h2 id=\"gravity-is-geometry-not-force\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#gravity-is-geometry-not-force\"></a>Gravity Is Geometry, Not Force</h2><p>Here's what Einstein figured out:</p>\n<ul>\n<li>Mass and energy <em>curve</em> spacetime.</li>\n<li>Objects move along the <em>straightest path</em> through this curved spacetime.</li>\n</ul>\n<p>That's it! No invisible rope pulling things in. Just geometry changing the rules of motion.</p>\n<p>A planet orbiting the Sun isn't being yanked by a force — it's moving in a straight line through a curved space. It looks like an orbit, but it's really just the planet following the <em>bend</em> in spacetime.</p>\n<h2 id=\"the-funnel-analogy--with-a-twist\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#the-funnel-analogy--with-a-twist\"></a>The Funnel Analogy — with a Twist</h2><p>Let's revisit the funnel analogy — but modify it.</p>\n<p>Yes, imagine a funnel — but now copy that funnel in every possible direction, forming a spherical warp around the mass. Instead of one drain in the middle, you've got a 3D pit from all angles.</p>\n<p>Even better: imagine being inside this warping. No slope to fall down, just space itself bending around you.</p>\n<p><strong>The Earth doesn't orbit the Sun because it's falling down a slope. It orbits because its straight-line path loops around inside this 3D curvature.</strong></p>\n<h2 id=\"bonus-what-would-gravity-look-like-if-you-could-see-it\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#bonus-what-would-gravity-look-like-if-you-could-see-it\"></a><em>Bonus:</em> What Would Gravity Look Like If You Could See It?</h2><p>It'd be wild.</p>\n<p>Imagine being able to see invisible wind patterns — not just wind on a surface, but in every direction. Gravity would look like <strong>smooth curves</strong> near stable systems (planets, stars). <strong>Swirling warps</strong> and eddies where fields overlap (like the Earth–Moon–Sun system). <strong>Ripples</strong> spreading out when massive events happen (like black holes colliding — gravitational waves).</p>\n<p>Each massive object would be like a stirring spoon in a cosmic fluid, and every smaller object would be caught in the flows.</p>\n<h2 id=\"speed-doesnt-change-the-curvature--just-how-you-move-through-it\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#speed-doesnt-change-the-curvature--just-how-you-move-through-it\"></a>Speed Doesn't Change the Curvature — Just How You Move Through It</h2><p>One final piece: the shape of the gravity field (spacetime curvature) comes from the mass and energy — not from how fast you're moving. Your speed just determines your path through that shape.</p>\n<p>So a satellite with just the right speed can loop endlessly in orbit. One with too little speed falls back. One with too much speed escapes. The terrain doesn't change — only your route through it does.</p>\n<h2 id=\"finally-so-what-is-gravity\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#finally-so-what-is-gravity\"></a><em>Finally:</em> So What Is Gravity?</h2><p>It's not a force pulling stuff down.\nIt's the shape of space and time, telling everything how to move.\nAnd if we could see it, it wouldn't be a clean grid with one funnel — it'd be a chaotic, rippling, warping ocean of invisible geometry.</p>\n<pre><code>Strangely beautiful.\nStill very hard to debug.</code></pre>",
            "url": "https://okuno.se/blog/what-does-gravity-actually-look-like",
            "title": "What Does Gravity Actually Look Like?",
            "summary": "You know how gravity is usually explained with that downwards funnel? A 2D grid, then someone plops a big bowling ball on it, and suddenly it's all marbles spinning into it. That analogy gets shown in every pop-science explainer ever — and for years, it totally threw me off.",
            "image": "https://okuno.se/blog/img/gravity.jpg",
            "date_modified": "2025-04-24T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/corepack-gotchas-with-pnpm-in-docker",
            "content_html": "<p>While working on a Docker setup for a Node.js project, I ran into a couple of subtle Corepack issues that are worth noting.</p>\n<h1 id=\"bypassing-the-corepack-is-about-to-download-prompt\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#bypassing-the-corepack-is-about-to-download-prompt\"></a>Bypassing the “Corepack is about to download” Prompt</h1><p>When running corepack inside Docker, it pauses with a prompt asking to download the package manager. Not ideal for automated builds. The fix is simple.\nSetting <code>COREPACK_ENABLE_DOWNLOAD_PROMPT=0</code> skips the download prompt and keeps things smooth in CI or Docker contexts.</p>\n<pre><code>RUN corepack enable &amp;&amp; corepack install --global pnpm@9.8.0\nENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0</code></pre><p><a href=\"https://github.com/nodejs/corepack/issues/550\" rel=\"noopener noreferrer\">source</a></p>\n<h1 id=\"cannot-find-matching-keyid-error\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#cannot-find-matching-keyid-error\"></a>“Cannot find matching keyid” Error</h1><p>While installing pnpm, Corepack threw a cryptic error:</p>\n<pre><code>throw new Error(`Cannot find matching keyid: ${JSON.stringify({ signatures, keys })}`);</code></pre><p>tl;dr: use latest corepack</p>\n<p>The signing keys in NPM were rotated and newly signed releases from pnpm caused Corepack to fail verification. In order to fix it you need to use Corepack <code>&gt;0.31.0</code> to install and run pnpm without it crashing (due to verify signature failure).</p>\n<p><a href=\"https://stackoverflow.com/questions/79411275/after-heroku-restart-pnpm-error-cannot-find-matching-keyid\" rel=\"noopener noreferrer\">source</a></p>\n<p>Here is the full error for context:</p>\n<pre><code>/usr/local/lib/node_modules/corepack/dist/lib/corepack.cjs:22688\n    throw new Error(`Cannot find matching keyid: ${JSON.stringify({ signatures, keys })}`);\n          ^\n\nError: Cannot find matching keyid: {\"signatures\":[{\"sig\":\"MEUCIQDI3E/8ZodnpZKAfKTeYe/+IF1wyawkKwK+0AkZodyJ9AIgQnQWC/XFtaz7eGusq5QPkYO0Vb412MsQcqxhadZ8IoA=\",\"keyid\":\"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U\"}],\"keys\":[{\"expires\":null,\"keyid\":\"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA\",\"keytype\":\"ecdsa-sha2-nistp256\",\"scheme\":\"ecdsa-sha2-nistp256\",\"key\":\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg==\"}]}\n    at verifySignature (/usr/local/lib/node_modules/corepack/dist/lib/corepack.cjs:22688:11)</code></pre>",
            "url": "https://okuno.se/blog/corepack-gotchas-with-pnpm-in-docker",
            "title": "Corepack Gotchas with pnpm in Docker",
            "summary": "Security can sometimes blow up in your face",
            "image": "https://okuno.se/blog/img/dev-frustrated-nodejs.jpg",
            "date_modified": "2025-04-08T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/When-JavaScript-Gets-Too-Bare",
            "content_html": "<p>I recently came across <a href=\"https://bare.pears.com/\" rel=\"noopener noreferrer\">Bare</a>, a new JavaScript runtime that takes minimalism to the extreme. It’s designed to let you “run JavaScript everywhere,” but without the bells and whistles of Node.js or Deno.</p>\n<p>First reaction? Intrigued then skeptical.</p>\n<h1 id=\"javascript-is-great---because-its-easy\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#javascript-is-great---because-its-easy\"></a>JavaScript Is Great - Because It’s Easy</h1><p>JavaScript (and TypeScript) continues to be my go-to language for many things. Not because it’s perfect, but because it’s simple and productive. The tooling, the ecosystem, and the fact that you can go from idea to prototype in a few minutes is powerful. It’s not the fastest or most efficient language, but with modern engines like V8, it’s good enough for most web and backend apps.</p>\n<p>Then enters Bare — essentially a JavaScript runtime without a standard library. No <code>fs</code>, no <code>net</code>, no <code>fetch</code>, no built-in utilities. It’s just you, your JS code, and a JavaScript engine like V8 (which you are responsible for integrating).</p>\n<p><em>So what’s the point?</em></p>\n<h1 id=\"how-bare-is-it\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#how-bare-is-it\"></a>How Bare Is It</h1><p>To understand where Bare fits, you have to look at what it’s trying to be:</p>\n<ul>\n<li>Not batteries-included: Unlike Node.js or Deno, Bare gives you nothing by default. You must define the runtime environment yourself.</li>\n<li>Highly modular: You decide what goes in, making it ideal for small, focused deployments.</li>\n<li>Cross-platform focused: It aims to run JavaScript in places JS usually doesn’t go—low-spec devices, embedded systems, mobile runtimes, and more.</li>\n</ul>\n<p>In that light, it starts to make sense. Bare isn’t trying to be your daily driver. It’s for when you want just enough JavaScript to get the job done and have full control over what’s included and how it behaves.</p>\n<h1 id=\"lets-talk-tradeoffs\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#lets-talk-tradeoffs\"></a>Let’s Talk Tradeoffs</h1><p>Here’s where the pragmatist in me kicks in.</p>\n<p>If I’m using JavaScript because it’s easy and fast to work with, then Bare removes much of what makes it appealing. I don't want to care about C and low-level systems when writing Javascript.</p>\n<p>At that point, the question becomes clear:</p>\n<blockquote>\n<p>If you have the knowledge and skill to go this low-level just to get JavaScript running, why not just use Golang?</p>\n</blockquote>\n<h1 id=\"why-golang-is-often-a-better-fit\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#why-golang-is-often-a-better-fit\"></a>Why Golang Is Often a Better Fit</h1><p>Golang offers a compelling alternative:</p>\n<ul>\n<li>Compiles to native code (no VM, no runtime)</li>\n<li>rich standard library</li>\n<li>low cpu and memory usage</li>\n</ul>\n<p>For most real-world apps that need to run on low-resource environments (think IoT devices, routers, small VMs), Go gives you all the control you need—with the productivity and ecosystem that Bare is intentionally missing.</p>\n<h1 id=\"so-when-is-bare-a-good-fit\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#so-when-is-bare-a-good-fit\"></a>So When Is Bare a Good Fit?</h1><p>To be fair, Bare does have a niche. It might be the right choice if you have an existing JS codebase you want to port to a constrained environment. Instead of rewriting the business logic you can rewrite the underlying platform. whatever makes more sense for your project.</p>\n<p>In that case, Bare is a great tool because it’s so minimal.</p>\n<h1 id=\"final-thoughts\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#final-thoughts\"></a>Final Thoughts</h1><p>Tools like Bare are important, not because everyone should use them, but because they push the boundaries of what’s possible. They give us control and minimalism when we really need it, even if it comes at the cost of convenience and developer experience.</p>\n<p>But for most developers looking to build reliable, efficient applications, especially in constrained environments, languages like Go are simply more practical. Bare removes the comfort that makes JavaScript appealing, and unless you’re deeply invested in JS or working in very specific contexts, you’re probably better off using a tool designed for that space from the start.</p>\n<p>At the end of the day, the tool doesn’t matter as much as the outcome. Choose what makes sense, not what’s trendy. Bare is fascinating — but for me, and probably many others, it’s a sharp tool for a narrow job.</p>\n",
            "url": "https://okuno.se/blog/When-JavaScript-Gets-Too-Bare",
            "title": "When JavaScript Gets Too Bare",
            "summary": "A Developer’s Perspective on the Bare Runtime",
            "image": "https://okuno.se/blog/img/bare_pear.jpg",
            "date_modified": "2025-04-07T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/syncthing-discovery-server-in-docker-with-caddy",
            "content_html": "<h2 id=\"why-run-your-own-discovery-server\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#why-run-your-own-discovery-server\"></a>Why run your own Discovery Server?</h2><p>Short answer: Well, why not? Self hosting! Slightly longer answer: In theory it\ncould speed up client discovery since you don't need to query the same\ninstance(s) as everyone else on the planet. And it can also be more secure to\nopt out of the global discovery server and only rely on your own server to make\nyour digital footprint slightly smaller. The official guide can be found\n<a href=\"https://docs.syncthing.net/users/stdiscosrv.html\" rel=\"noopener noreferrer\">here</a>. Here is further\nreading on the\n<a href=\"https://docs.syncthing.net/users/security.html\" rel=\"noopener noreferrer\">security behind the Syncthing infrastructure</a>.</p>\n<h2 id=\"1-run-discovery-server-as-docker-container\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#1-run-discovery-server-as-docker-container\"></a>1. Run Discovery Server as Docker Container</h2><p>The docker container can be found in Docker Hub under the\nname<a href=\"https://hub.docker.com/r/syncthing/discosrv\" rel=\"noopener noreferrer\">syncthing/discosrv</a>. I am\nrunning it using the following bash script:</p>\n<pre><code>NAME=syncthing-discovery-server\nVERSION=1.23.6\n\ndocker run \\\n  --name $NAME \\\n  -d \\\n  -p 127.0.0.1:8443:8443 \\\n  --restart=unless-stopped \\\n  --network shared_docker-network \\\n  syncthing/discosrv:$VERSION -http \\</code></pre><p>Note that I am adding this container to a specific network and I am only\nexposing the port to localhost. In this network I am also running a caddy server\nwhich will be running a reverse proxy and handling the TLS/HTTPS requests. The\ndiscovery server needs TLS in order to operate securely. Since I am lazy I will\nuse Caddy to solve this issue for me. All I need to do is to setup an A or CNAME\nrecord which points to my server and Caddy handles the certificates.</p>\n<h2 id=\"2-setup-caddy-as-reverse-proxy\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#2-setup-caddy-as-reverse-proxy\"></a>2. Setup Caddy as Reverse Proxy</h2><p>As mentioned above, Caddy will handle TLS. The following Caddyfile will reverse\nproxy all request with <em>your.server.com</em> to the Syncthing Discover Server\nrunning as a Docker Container in the same Docker network as the\n<a href=\"../caddy-in-docker-with-common-log\" rel=\"noopener noreferrer\">Caddy Docker Container</a>. This Caddyfile is\ninspired by this old\n<a href=\"https://github.com/syncthing/docs/issues/631#issuecomment-814730466\" rel=\"noopener noreferrer\">Github comment</a>.\nBut, one of the header is outdated now. The new <code>header_up</code> line to be used is:\n<code>header_up X-Tls-Client-Cert-Der-Base64 {http.request.tls.client.certificate_der_base64}</code>.\nThis is pointed out in a\n<a href=\"https://github.com/caddyserver/caddy/pull/4241#issuecomment-1328171418\" rel=\"noopener noreferrer\">newer comment on a PR</a>.\nPlease see the whole file below</p>\n<pre><code>your.server.com {\n  reverse_proxy syncthing-discovery-server:8443 {\n    header_up X-Forwarded-For {http.request.remote.host}\n    header_up X-Client-Port {http.request.remote.port}\n    header_up X-Tls-Client-Cert-Der-Base64 {http.request.tls.client.certificate_der_base64}\n  }\n\n  tls {\n    client_auth {\n      mode request\n    }\n  }\n}</code></pre><h2 id=\"3-point-your-syncthing-client-to-your-discovery-server\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#3-point-your-syncthing-client-to-your-discovery-server\"></a>3. Point your Syncthing Client to your Discovery Server</h2><p>Go to <code>Settings &gt; Connections &gt; Global Discovery Servers</code> and make a comma and\nadd the full URL (e.g. <a href=\"https://your.server.com\" rel=\"noopener noreferrer\">https://your.server.com</a>) to your Discovery Server. DONE!</p>\n<h2 id=\"notable-mention---t4skforce\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#notable-mention---t4skforce\"></a>Notable mention - t4skforce</h2><p>Before we start, I did not try the docker image\n<a href=\"https://hub.docker.com/r/t4skforce/syncthing-discovery\" rel=\"noopener noreferrer\">t4skforce/syncthing-discovery</a>\n([Github(<a href=\"https://github.com/t4skforce/syncthing-discovery)]\" rel=\"noopener noreferrer\">https://github.com/t4skforce/syncthing-discovery)]</a>). This image also\nruns the Discovery Server with a reverse proxy, but you still need to provide\nthe SSL certificate yourself.</p>\n<h2 id=\"versions\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#versions\"></a>Versions</h2><p>This writeup was using the following versions for the included software</p>\n<ul>\n<li>Syncthing <code>1.23.6</code></li>\n<li>Caddy <code>2.6.2</code></li>\n</ul>\n<h2 id=\"resources\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#resources\"></a>Resources</h2><ul>\n<li><a href=\"https://docs.syncthing.net/users/stdiscosrv.html\" rel=\"noopener noreferrer\">Manual</a></li>\n</ul>\n",
            "url": "https://okuno.se/blog/syncthing-discovery-server-in-docker-with-caddy",
            "title": "Syncthing Discovery Server in Docker with Caddy",
            "summary": "On a journey to discover your own infrastructure",
            "image": "https://okuno.se/blog/syncthing-discovery-server-in-docker-with-caddy/discover.jpg",
            "date_modified": "2023-07-28T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/rocket-chat-mongodb-raspberry-pi-arm",
            "content_html": "<p>I tried to install <a href=\"http://Rocket.chat\" rel=\"noopener noreferrer\">Rocket.chat</a> today but failed/gave up\ndue to hardware limitations with RPi4 (Raspberry\nPi 4)<sup><a href=\"#1\">[1]</a></sup>. However\n<a href=\"https://en.wiktionary.org/wiki/TIL\" rel=\"noopener noreferrer\">TIL</a> about ARM instruction set (micro\nserver architecture) in the process.</p>\n<h2 id=\"first-boss-mongodb\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#first-boss-mongodb\"></a>First Boss: MongoDb</h2><p>The problem began when I was trying to get MongoDb running on the RPi4. Not the\nlatest version, but recent version would not start. After a while I understood\nthat MongoDb targets a\n<a href=\"https://github.com/docker-library/mongo/issues/510#issuecomment-970862957\" rel=\"noopener noreferrer\">minimum instruction set of (<code>ARMv8.2-A</code>)</a>\nwhich is later instruction set than what\n<a href=\"https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications\" rel=\"noopener noreferrer\">RPi4 implements (<code>ARMv8-A</code>)</a>.\nHowever I could still run a previous version of the major 4 version (<code>4.2.18</code>)\nwhich was before the switch (as of writing <code>6.0.5</code> is the latest version)\nHowever this solution is not very future proof and probably means that updating\nRocket.chat is limited.</p>\n<h2 id=\"second-boss-rocketchat-spoiler-alert-game-over\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#second-boss-rocketchat-spoiler-alert-game-over\"></a>Second Boss: Rocket.Chat (spoiler alert: Game Over)</h2><p>After MongoDB was up and running my focus shifted to\n<a href=\"http://rocket.chat\" rel=\"noopener noreferrer\">rocket.chat</a> itself where I ran into the <em>same</em> issue.\nThere were no support for ARM when running the latest version Rocket.chat. I\nthought it was a bit weird since it’s just a node.js program... However there is\na 3 year old half decent instruction to run an older version,\n<a href=\"https://github.com/RocketChat/Rocket.Chat.Embedded.arm64\" rel=\"noopener noreferrer\">but it wasn’t working out of the box</a>.\nAfter some searching on their GitHub issues and their website forum I realized\nthat ARM support was not a priority nor something the core team was interested\nin.</p>\n<h2 id=\"side-quest-mini-pc\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#side-quest-mini-pc\"></a>Side Quest: Mini-PC</h2><p>As a side quest I started looking into mini-pc since I remember a\n<a href=\"https://news.ycombinator.com/item?id=35831087\" rel=\"noopener noreferrer\">comment on hacker news</a> giving a\ntip about\n<a href=\"https://store.mele.cn/products/mele-fanless-mini-pc-quieter3q-n5105-windows-11-pro-micro-computer-8gb-ddr4-256gb-rom-small-desktop-computers-for-office-home-dual-hdmi-4k-60hz-bt5-2-wi-fi-6-usb3-0-ethernet-port-vesa-mount\" rel=\"noopener noreferrer\">MeLe Quieter3Q</a>\n(intel N5105 processor). It costs <code>¥35.000</code>-<code>¥40.000</code> which is more than double\nthe RPi4 (there are cheaper mini-pc but they have a loud fan) but it’s a x86-64\nprocessor which just makes everything so much easier for server development...\nWhat makes this something interesting is that the wattage is supposed to be very\nsimilar to RPi4 which is one of the main reasons I went for the RPi4 as my home\nserver solution, while also having a virtual (x86-x64) server at Digital Ocean\nas my main server.</p>\n<p>RPi4 - 5W (idle 3W) source:\n<a href=\"https://www.pidramble.com/wiki/benchmarks/power-consumption\" rel=\"noopener noreferrer\">source1</a>,\n<a href=\"https://www.reddit.com/r/homeassistant/comments/li1f8c/pi4_vs_mini_pc/\" rel=\"noopener noreferrer\">source2</a></p>\n<p>Quieter3Q - 11W (idle 3W) source:\n<a href=\"https://www.cnx-software.com/2022/06/03/mele-quieter3q-review-ultra-thin-fanless-mini-pc-tested-with-windows-11-ubuntu-22-04/\" rel=\"noopener noreferrer\">source1</a></p>\n<p>At this very moment I don’t have the need (nor the time #dad) to tinker with a\nmini PC but it’s definitely something to note for future needs.</p>\n<h2 id=\"footnotes\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#footnotes\"></a>Footnotes</h2><h3 id=\"1\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#1\"></a>1.</h3><ul>\n<li><a href=\"https://github.com/RocketChat/Rocket.Chat/issues/27305\" rel=\"noopener noreferrer\">arm64 Docker images</a></li>\n<li><a href=\"https://github.com/RocketChat/Rocket.Chat/issues/23722\" rel=\"noopener noreferrer\">Outdated snap packages for armhf and arm64</a></li>\n</ul>\n",
            "url": "https://okuno.se/blog/rocket-chat-mongodb-raspberry-pi-arm",
            "title": "Rocket.chat and MongoDB (not) on an RPi4",
            "summary": "Failing to run a chat program on an ARM processor",
            "image": "https://okuno.se/blog/rocket-chat-mongodb-raspberry-pi-arm/rocket.jpeg",
            "date_modified": "2023-05-10T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/caddy-in-docker-with-local-time",
            "content_html": "<h2 id=\"caddy-does-not-ship-tzdata\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#caddy-does-not-ship-tzdata\"></a>Caddy does <em>not</em> ship <code>tzdata</code></h2><p>Apparently Caddy does not read the <code>TZ</code> enviornment variable. See:\n<a href=\"https://caddy.community/t/set-timezone-for-caddy-2-official-docker-image/8622\" rel=\"noopener noreferrer\">https://caddy.community/t/set-timezone-for-caddy-2-official-docker-image/8622</a></p>\n<p>In order to get logs in local time you need to\n<a href=\"https://github.com/caddyserver/xcaddy\" rel=\"noopener noreferrer\">build your own image</a>. I started\nbuilding my own <code>Dockerfile</code> in the blog post:\n<a href=\"caddy-in-docker-with-common-log\" rel=\"noopener noreferrer\">Caddy in Docker with common_log</a>. This is what\nwe need to add in order for <code>TZ</code> env var to work:</p>\n<pre><code>RUN apk add --no-cache tzdata</code></pre><p>Which makes the full <code>Dockerfile</code>:</p>\n<pre><code>ARG VERSION=2.6.2\n\nFROM caddy:${VERSION}-builder AS builder\n\nRUN xcaddy build \\\n  --with github.com/caddyserver/transform-encoder\n\nFROM caddy:${VERSION}\n\nRUN apk add --no-cache tzdata # &lt;-- ADD THIS\n\nCOPY --from=builder /usr/bin/caddy /usr/bin/caddy</code></pre><h2 id=\"forking-and-modifying-transform-encoder\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#forking-and-modifying-transform-encoder\"></a>Forking and modifying <code>transform-encoder</code></h2><p><em>Section spoiler: This was a dead end :)</em></p>\n<p>I <a href=\"https://github.com/MrOggy85/transform-encoder\" rel=\"noopener noreferrer\">forked</a> the\n<code>transform-encoder</code> repo and hardcoded <code>TimeLocal = True</code>. I think it was a fun\nexercise to write some <a href=\"https://go.dev/learn/\" rel=\"noopener noreferrer\">Go</a> which I don't do so often. I\ncould successfully make the change and then tried to build a new caddy image.\nHowever, I ran into an error:</p>\n<pre><code>module declares its path as: github.com/caddyserver/transform-encoder\n        but was required as: github.com/MrOggy85/transform-encoder</code></pre><p>In order to replace the module with your fork you need to write your Dockerfile\nusing <code>=</code> like this:</p>\n<pre><code>RUN xcaddy build \\\n  --with github.com/caddyserver/transform-encoder=github.com/MrOggy85/transform-encoder@master</code></pre><p>I was finally able to build a new caddy image, ran it, but no change... Back to\nWhoogle and I finally found out what I statated in the beginning of this post,\nthat you just need to add/install <code>tzdata</code>.</p>\n<h2 id=\"tldr\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#tldr\"></a>tl;dr</h2><p>Here is the image if you want to use it right away:</p>\n<pre><code>docker pull mroggy85/caddy:2.6.2</code></pre><p>See my Docker Hub Page: <a href=\"https://hub.docker.com/r/mroggy85/caddy\" rel=\"noopener noreferrer\">https://hub.docker.com/r/mroggy85/caddy</a></p>\n",
            "url": "https://okuno.se/blog/caddy-in-docker-with-local-time",
            "title": "Caddy in Docker with Local Time",
            "summary": "When you want to log in your time zone",
            "image": "https://okuno.se/blog/caddy-in-docker-with-local-time/flying-whale-container-ship.jpeg",
            "date_modified": "2022-11-21T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/passwords-how-to-store-salt",
            "content_html": "<p>This is the\n<a href=\"https://security.stackexchange.com/questions/17421/how-to-store-salt\" rel=\"noopener noreferrer\">link</a> for\nthe full read.</p>\n<blockquote>\n<p>TL;DR - You can store the salt in plaintext without any form of obfuscation or\nencryption, but don't just give it out to anyone who wants it.</p>\n<p><a href=\"https://security.stackexchange.com/a/17435/70970\" rel=\"noopener noreferrer\">Polynomial</a></p>\n</blockquote>\n<p>The reason we want to salt the password is to make it <em>harder</em> for an attacker\nto crack the passwords, <strong>once the DB has been comprimised</strong>. When the password\nhas been encrypted with the salt, an attacker can't use a\n<a href=\"http://en.wikipedia.org/wiki/Rainbow_table\" rel=\"noopener noreferrer\">rainbow table</a>. This means that the\nattacker needs to spend a lot of time decrypting the passwords. This gives you\ntime to detect the breach and reset all the passwords.</p>\n<p>Read the full answer <a href=\"https://security.stackexchange.com/a/17435/70970\" rel=\"noopener noreferrer\">here</a>\nfor a full technical deep dive.</p>\n",
            "url": "https://okuno.se/blog/passwords-how-to-store-salt",
            "title": "Passwords - How to store salt?",
            "summary": "TI(r)L (Today I really Learned) how (and why) to store salt.",
            "image": "https://okuno.se/blog/passwords-how-to-store-salt/salt-fingers.jpeg",
            "date_modified": "2022-11-10T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/postgres-http-proxy",
            "content_html": "<h2 id=\"the-narrow-usage-case\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#the-narrow-usage-case\"></a>The (narrow) usage case</h2><p>You are hacking away on a personal website using React or any other SPA\ntechnology. You also need to write a backend service to connect to your DB in\norder to persist your data across your different devices and client. However,\nimagine if you could just use a general backend to directly connect to your DB?</p>\n<h2 id=\"how\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#how\"></a>How?</h2><p>Deploy a HTTP server that accepts POST requests with a JSON body. The body needs\nto include the credentials and location of your DB as well as the SQL you want\nto execute. Then you just connect to the DB using the credentials from the\nrequest and execute the query. Return the result and you are done.</p>\n<h2 id=\"usage-example\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#usage-example\"></a>Usage Example</h2><p>It's very convenient to use for a web client. See this example using <code>axios</code>:</p>\n<pre><code>type PostgresProxyResponse = {\n  command: \"SELECT\";\n  query: unknown;\n  rowCount: number;\n  rows: any[][];\n};\n\nasync function request(q: string) {\n  const response = await axios({\n    url: \"/\",\n    baseURL: HTTP_ADDRESS_OF_ADEPLOYED_API,\n    method: \"POST\",\n    data: {\n      u: \"DB_USER\",\n      pw: \"DB_PASSWORD\",\n      h: \"DB_HOST\",\n      port: \"DB_PORT\",\n      db: \"DB_NAME\",\n      q: \"SELECT * FROM table\",\n    },\n  });\n\n  return response.data;\n}</code></pre><h2 id=\"security-concerns\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#security-concerns\"></a>Security concerns</h2><p>This is a short-cut solution for personal web projects to enable rapid\ndevelopment. This assumes you are consuming a Postgres DB that is open to the\ninternet. If you are developing a web service for other users, it means that\nthey need to establish connection to their own DB. Otherwise you need to include\nthe DB credentials (in plaintext) for all users to see.</p>\n<p>Needles to say, since this solution pushes the DB credentials down to the\nclient, it's not a very secure solution, unless you trust the client, since you\nwrote it yourself.</p>\n<p>Another issue is that you need to run your own proxy to be safe. Right now I am\nnot logging the credentials, but it's easy for a malicient service to store your\ncrentials. Also, since you are sending your DB credentials over the wire, make\nsure it's HTTPS!</p>\n<h2 id=\"performance-concerns\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#performance-concerns\"></a>Performance concerns</h2><p>The API is static, which means it will open and close a DB connection for each\nrequest. Therefor it adds extra latency. Another performance concern is the\nmultiple protocols involved and data tranformation happening for each SQL\ncommand. With that said, this is obviously not a perforant nor a scalable\ntechnical solution. However, for the very small scale it's intended for it's\nsuffice.</p>\n<p>This is very handy if you like your website to be able to send SQL commands\ndirectly to the DB. It's very useful for small personal projects. But, it's\ndefinitly not recommended for any service with multiple users because of obvious\nsecurity concerns. Please use it at your own risk.</p>\n<h2 id=\"take-away\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#take-away\"></a>Take away</h2><p>This is far from rocket science, and someone probably already created this.\nHowever, I didn't find this solution after some (Brave) searching so I wrote it\ninstead.</p>\n<p>See Github: <a href=\"https://github.com/MrOggy85/postgres-proxy\" rel=\"noopener noreferrer\">https://github.com/MrOggy85/postgres-proxy</a></p>\n",
            "url": "https://okuno.se/blog/postgres-http-proxy",
            "title": "Postgres HTTP proxy",
            "summary": "Lightweight HTTP proxy for sending SQL commands to a Postgres DB",
            "image": "https://okuno.se/blog/postgres-http-proxy/squid.png",
            "date_modified": "2022-09-25T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/caddy-in-docker-with-common-log",
            "content_html": "<p>As of Caddy <a href=\"https://github.com/caddyserver/caddy/releases/tag/v2.5.0\" rel=\"noopener noreferrer\">v2.5.0</a>\nthey have <em>\"removed the deprecated <code>common_log</code> field from HTTP access logs, and\nthe <code>single_field</code> encoder.\"</em>. For me as a casual user of Caddy who mostly just\nlooks at logs this is inconvenient. I don't use any tools to analyse json files\nfor automation, maybe I should? However, the good folks at Caddy doesn't leave\nus high and dry. <em>\"If you relied on this, you may use the\n<a href=\"https://github.com/caddyserver/transform-encoder\" rel=\"noopener noreferrer\"><code>transform encoder plugin</code></a>\nto encode logs in Common Log format.\"</em>. But, since I am using Docker the version\nof Caddy I pull down from Docker Hub does not have this plugin available...\nEnter the builder.</p>\n<p>According to\n<a href=\"https://hub.docker.com/_/caddy/\" rel=\"noopener noreferrer\">Caddy's official Docker Hub documentation</a>, we\nneed to create our own\n<a href=\"https://docs.docker.com/engine/reference/builder/\" rel=\"noopener noreferrer\">Dockerfile</a> with the builder\nimage (<code>caddy:builder</code>) and then create our <strong>own</strong> Caddy image.</p>\n<p>This is how the Dockerfile looks like:</p>\n<pre><code>ARG VERSION=2.5.2\n\nFROM caddy:${VERSION}-builder AS builder\n\nRUN xcaddy build \\\n  --with github.com/caddyserver/transform-encoder\n\nFROM caddy:${VERSION}\n\nCOPY --from=builder /usr/bin/caddy /usr/bin/caddy</code></pre><p>The magic part is the line:</p>\n<pre><code>RUN xcaddy build \\\n  --with github.com/caddyserver/transform-encoder</code></pre><p>Which pulls the <code>transform-encoder</code> plugin and installs it. Then we are\nswitching to a \"normal\" Caddy image and just replacing the binary with our\ncustom built one.</p>\n<p>Now you just need to build and run this Dockerfile locally!</p>\n<p><img src=\"caddy-in-docker-with-common-log/container.jpeg\" alt=\"docker whales\" /></p>\n<h2 id=\"caddyfile\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#caddyfile\"></a>Caddyfile</h2><p>This is the syntax to use it in a\n<a href=\"https://caddyserver.com/docs/caddyfile/concepts#caddyfile-concepts\" rel=\"noopener noreferrer\">Caddyfile</a>:</p>\n<pre><code>log {\n        format transform \"{common_log}\"\n        output file /var/log/homepage.log\n    }</code></pre><h1 id=\"tldr\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#tldr\"></a>tl;dr</h1><pre><code>docker pull mroggy85/caddy:2.5.2</code></pre><p>See my Docker Hub Page: <a href=\"https://hub.docker.com/r/mroggy85/caddy\" rel=\"noopener noreferrer\">https://hub.docker.com/r/mroggy85/caddy</a></p>\n",
            "url": "https://okuno.se/blog/caddy-in-docker-with-common-log",
            "title": "Caddy in Docker with common_log",
            "summary": "When you just want a simple log",
            "image": "https://okuno.se/blog/caddy-in-docker-with-common-log/caddy_in_docker.jpeg",
            "date_modified": "2022-07-14T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/iterm2-triggers",
            "content_html": "<blockquote>\n<p>created using <a href=\"https://www.craiyon.com/\" rel=\"noopener noreferrer\">craiyon.com</a></p>\n</blockquote>\n<p><a href=\"https://www.urbandictionary.com/define.php?term=TIL\">TIL</a>\nabout\n<a href=\"https://iterm2.com/documentation-triggers.html\">iTerm2 <strong>Triggers</strong></a>\nwhich is a super easy way to super charge your terminal. It's a feature that\nlet's you highlight text, create links and much more!</p>\n<p>You can find it in the settings here:\n<img src=\"iterm2-triggers/iterm2-trigger-settings.jpg\" alt=\"iTerm2 Settings Screenshot\" width=\"1846\" height=\"1078\" /></p>\n<h2 id=\"highlight-ip-address\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#highlight-ip-address\"></a>Highlight IP Address</h2><p>One thing that I have added is to highligh IP Addresses, which makes it very\neasy to distinguish addresses when glancing over a log.</p>\n<p>Regular Expression:\n<code>(\\b25[0-5]|\\b2[0-4][0-9]|\\b[01]?[0-9][0-9]?)(\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}</code></p>\n<p><img src=\"iterm2-triggers/ssh_log.jpg\" alt=\"High Level Diagram\" width=\"1496\" height=\"768\" /></p>\n<blockquote>\n<p>Look at all those hacker attempts... Now in gold color!</p>\n</blockquote>\n<h2 id=\"link-to-jira\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#link-to-jira\"></a>Link to Jira</h2><p>Another nice feature is to add a clickable link to the Jira ticket. For example\nwhen you check the git log you can now directly just shift+click and come\ndirectly to the Jira ticket in the browser.</p>\n<ul>\n<li>Regular Expression: <code>(?i)wf-\\d+</code> (replace \"wf\" with your Jira project's key)</li>\n<li>Action: <code>Make Hyperlink</code></li>\n<li>Parameters <code>https://jira.your-company.jp/browse/\\0</code> (replace \"your-company\"\nwith your company's subdomain)</li>\n</ul>\n<p><img src=\"iterm2-triggers/hacker.png\" alt=\"Terminal Hacker\" width=\"256\" height=\"256\" /></p>\n<blockquote>\n<p>created using <a href=\"https://www.craiyon.com/\" rel=\"noopener noreferrer\">craiyon.com</a></p>\n</blockquote>\n",
            "url": "https://okuno.se/blog/iterm2-triggers",
            "title": "iTerm2 Triggers",
            "summary": "'Supercharge your terminal'",
            "image": "https://okuno.se/blog/iterm2-triggers/pc-with-rainbow.png",
            "date_modified": "2022-06-22T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/perfect-code",
            "content_html": "<p>Code that doesn't need change. Code that stand the test of time. Code that is\ngeneric enough that it will serve <em>all</em> future use cases.</p>\n<ul>\n<li>Why strive for this type of code?</li>\n</ul>\n<p>We devs are usually both lazy and perfectionists. If we could only write these\nperfect pieces of\n<a href=\"https://en.wikipedia.org/wiki/Don't_repeat_yourself\">DRY (Do not Repeat\nYourself)</a> business logic and helper function then we can use them as Lego\nblocks going forward. Then we could handle any business requirements, new or\nchanged, <em>in theory</em>.</p>\n<p>Having a file with frequent changes is considered a bad thing by a lot of\nvalidation tools. And why is that bad? Because we want to our code to reach a\n<em>stable state</em>. <strong>Write and forget</strong>. If we need to go back to the same place\nand make a change we missed an opportunity the first time to make it right.</p>\n<ul>\n<li>We have now wasted time. <em>Right</em>?</li>\n</ul>\n<p>However, I think this mindset is not very helpful when writing a business\napplication. <em>Why</em>? Because the <strong>business requirments change all the time</strong>.\nThe very reason for our code changes. This is nothing new, this is the basis of\nthe <a href=\"https://agilemanifesto.org/principles.html\">agile methology</a>.\nHowever, the agile mindset is in conflict with the dev mentality and the persuit\nof perfect code.</p>\n<blockquote>\n<p>The reason we code is because we want to achieve something. If we want to\nachieve something different we need to change the code.</p>\n</blockquote>\n<p>We have to embrace the fact that <strong>our code will never be finished</strong>, since the\nreason, (business, competition, economy, etc), <em>is ever-changing</em>. This is the\nessence of\n<a href=\"https://www.agilealliance.org/agile101/12-principles-behind-the-agile-manifesto/\">\"embrace\nchange\" (2.) from the agile manifesto</a>. Just follow the logic; The reason we\ncode is because we want to achieve something. If we want to achieve something\ndifferent we need to change the code.</p>\n<blockquote>\n<p>in order to write good code we need to have good communication with business</p>\n</blockquote>\n<p>It all comes down to human communication, something that we devs are naturally\nbad at. A lot of us devs came to the field because we like to talk to computers\nrather than flimsy meat bags. But, in order to write good code we need to have\ngood communication with business. The quality of code cannot be soly determined\nby looking at the code, you have to also look at the purpose it serves. That's\nwhy code analytics tools can easily lead us astray. They don't take business\nrequirments into consideration. Good code may smell, but if the reason for the\ncode frequently change, what do u achieve by \"fixing\" it?</p>\n<blockquote>\n<p>Generic code only works if we anticipate business changes.</p>\n</blockquote>\n<p>Writing <em>specific</em> code rather than <em>generic</em> code will help you to quickly\nfulfill business requirments. Generic code only works if we anticipate business\nchanges. While there are many best practices to make generic code it all boils\ndown to guess-work. To be able to figure out what business wants in the future\nis similar to betting. You can take calculated risks with better communications\nwith business to get a better understanding of upcoming changes.</p>\n<p>We are better of if we <strong>concede the notion of perfect code</strong> and instead focus\non easily refactorable, extensible code or even code that can be thrown away.\nThis goes against programmer culture which glorifies e.g. old Unix programs that\nhave been around for centuries and doesn't need patches since the are\n<em>considered done</em>. <em>Perfect</em>. The author got it right.</p>\n<p>While the persuit for perfect code is admirable and true in some sense, it\ndoesn't serve us as a guiding light. Instead, focusing on the reason will better\nguide our coding decision making. And since the reason is ever changing, your\ncode needs to change with it.</p>\n",
            "url": "https://okuno.se/blog/perfect-code",
            "title": "Perfect Code",
            "summary": "'The futile persuit of perfect code in a business application.'",
            "image": "https://okuno.se/blog/perfect-code/alexander-sinn-KgLtFCgfC28-unsplash.jpeg",
            "date_modified": "2022-06-14T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/the-web3-we-dont-need",
            "content_html": "<p>In blockchain tech community there are a lot of\n<a href=\"https://mirror.xyz/suzuha.eth/vb5E5lhzmPTcpxOJcz6Q211TDgSvoFwDLA6JSM1V37Q\">really\nsmart people</a> working on very technically sophisticated solutions to very\ncomplex systems and problems. They are trying to fix the issues of today while\nanticipating the issues of tomorrow. They also have the financial backing of the\nvalue of the cryptocurrency that is flowing on top of the blockchain technology\nthey are working on.</p>\n<h2 id=\"we-have-a-communication-issue\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#we-have-a-communication-issue\"></a>We have a communication issue</h2><blockquote>\n<p>What's the value added?</p>\n</blockquote>\n<p>The tech people only focuses on the technical solution. The financial people\nonly focuses on the speculation market. Both are pointing in a positive\ndirection. So from their perspective they can't fathom why they are\n<a href=\"https://blog.mollywhite.net/blockchains-are-not-what-they-say/\">receiving\nso much criticism.</a></p>\n<blockquote>\n<p>Yes, blockchain technology is cool, but it does not meet the expectation it\nsets on itself.</p>\n</blockquote>\n<p>The cristism boils down to: <strong>What's the value added?</strong> It seems like the for\nthe majority of the solutions currently on the market, there would be a\n<em>negative</em> value for switching to blockchain technology. There <em>may</em> be a new,\nnische, market for blockchain technology. This is were the divide between\nblockchain lovers 🥰 and haters 😤 are the greatest. Web3 is pitched to be a\nrevlutionary technology which is going to replace the current state. This bold\nclaim requires ground breaking technology. Looking from the outside, there is\nnothing that even remotely meets that standard when it comes to blockchain\ntechnology. This is the heart of the issue. Yes, blockchain technology is cool,\nbut it does not meet the expectation it sets on itself.</p>\n<blockquote>\n<p>It is a solution in search of a problem.</p>\n</blockquote>\n<p>The main innovation is the distributed leger. Yes, it removes central control,\n<a href=\"https://moxie.org/2022/01/07/web3-first-impressions.html\"> <em>in\ntheory</em></a>. Yes, it requires more resources than a traditional database, but\nthat's the trade-off. However, is this a technical solution that is interesting\nfor most mobile apps, SaaS, social media, websites, games and other technical\nservices and products out there? No, not really. The current problems are not\nsolved with this technological innovation. It is a solution in search of a\nproblem.</p>\n<h2 id=\"cant-separate-blockchain-and-cryptocurrency-speculation\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#cant-separate-blockchain-and-cryptocurrency-speculation\"></a>Can't Separate Blockchain and Cryptocurrency Speculation</h2><p>I view blockchain technology as separate from the value of cryptocurrency. But,\nI don't think they can be separated. They are driving each other forward. The\nhype of blockchain leads to an increase in value of cryptocurrency. The\nincreased value of cryptocurrency leads to an increase to the hype of\nblockchain. And the circle goes on... There is a promise of future market\nadaptation of blockchain/cryptocurrency technology. This is where web3 comes in.\nI want to make a distinction between blockchain and web3. From a technical point\nof view it's a suttle one, but from a community point of view it's a\ndifferentiator worth noting.</p>\n<h3 id=\"definition-of-blockchain-technology-and-the-people-working-in-this-space\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#definition-of-blockchain-technology-and-the-people-working-in-this-space\"></a>Definition of blockchain technology and the people working in this space</h3><p>These are all very smart people, working on a grand vision with high principles.\nIt's a very admirable cause and an exciting future, if it ever would be\nrealized. It's a space that has been around for at least 14 years, since Bitcoin\nemerged 2008 (I guess you can say it started earlier or later depending on your\ndefinition, but this is what I am going with here). Over the years a lot of\ninnovations has happened, new blockchains have emerged with different technical\nsolutions. New solutions on top of blockchains have emerged like smart contracts\nand NFTs. Here is where web3 definition stsrts</p>\n<h3 id=\"definition-of-web3\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#definition-of-web3\"></a>Definition of Web3</h3><p>I see web3 as an evolution of the blockchain and cryptocurrency community. With\na very important addition, the non-techy, financial speculative persons. There\nwas a realization that a lot of money could be made in this space. Add som fomo\nand trending social media posts. All of these people were very influencal. They\nhad capital and good reputation. But, in order to sustain the flow of cash, the\ntechnology needed to be inflated. Big promises were made that can't be backed up\nwith the reality of the technology.</p>\n<h2 id=\"possible-future\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#possible-future\"></a>Possible Future</h2><p>This is my prediction: When investors realize that blockchain and cryptocurrency\nwill not reach the anticipated market adaption, we will see a meltdown in\ninvestments in companies working on blockchain technology and the hype over\nblockchain will be over. Blockchain and cryptocurrency will still be around of\ncourse, but the talk about web3 as the next step in the evolution of the web\nwill die out. DFi, or \"Decentralized Finance\" will still be around since that's\nthe main application of blockchain technology, interacting with the transactions\non the ledger. But, that's the limit and were mass adoptation of blockchain tech\nwill end.</p>\n",
            "url": "https://okuno.se/blog/the-web3-we-dont-need",
            "title": "The Web3 that we don't need",
            "summary": "'It seems to me like a perfect storm of tech, greed and smart people.'",
            "image": "https://okuno.se/blog/the-web3-we-dont-need/clark-van-der-beken-mcCAFvlzZFQ-unsplash.jpeg",
            "date_modified": "2022-01-12T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/photo-blog-with-nextjs-deno-syncthing",
            "content_html": "<h2 id=\"why-build-a-photo-blog-in-2021\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#why-build-a-photo-blog-in-2021\"></a>Why build a Photo Blog in 2021?</h2><p>Nowadays Instagram is the standard for photo blogging. I really don't like\nInstagram for a number of reasons including giving my data to the Facebook\nalgorithm, and the whole idea of sharing with everyone by default. I recently\nlooked into an open source alternative: <a href=\"https://pixelfed.org/\" rel=\"noopener noreferrer\">Pixelfed</a> and\ntried out running <a href=\"https://pixelfed.oskarlindgren.se\" rel=\"noopener noreferrer\">my own server</a>. It's a\nnice service, but to share my photos with my family and friends the barrier of\nentry is too high. Another alternative is to run a Ghost blog, which I have\ntried. However, posting a new entry with pictures is pretty tedious and I found\nmyself not posting as much as I wanted.</p>\n<p>What I am trying to achieve is pretty simple: Just a website where each page is\nan album with pictures of my everyday life that can be viewed by my close family\nand friends, <em>if they want</em>. And the most important: A very very easy way to\npublish new photos and albums.</p>\n<h2 id=\"overview\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#overview\"></a>Overview</h2><p><img src=\"photo-blog-nextjs-deno-syncthing/high-level-diagram.png\" alt=\"High Level Diagram\" width=\"1320\" height=\"796\" /></p>\n<p>Technologies used:</p>\n<ul>\n<li><a href=\"https://nextjs.org/\" rel=\"noopener noreferrer\">Next.js</a></li>\n<li><a href=\"https://deno.land/\" rel=\"noopener noreferrer\">Deno</a></li>\n<li><a href=\"https://syncthing.net/\" rel=\"noopener noreferrer\">Syncthing</a></li>\n</ul>\n<p>From an end users perspective there is nothing interesting. Just a website that\nconnects to an API which serves some images.</p>\n<p>From the website admin point of view it gets more interesting. Let's say you\nwant to upload a new album with some images. Just copy the images on your phone\nto the special folder on your phone, wait a couple of seconds and boom! Your new\nalbum has been published! But how does it work?</p>\n<h3 id=\"syncthing---sync-photos\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#syncthing---sync-photos\"></a>Syncthing - Sync Photos</h3><p>Syncthing is a peer-2-peer file syncing service which doesn't require any\ncentral server like e.g. Dropbox. For this project I have a folder on my server\nthat is synced with a folder on my smartphone. This can also be synced with\nanother device if you want to edit/add photos from another device.</p>\n<h3 id=\"deno---serve-photos\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#deno---serve-photos\"></a>Deno - Serve Photos</h3><p>Deno is the new kid on the block that will eventually replace Node.js. With deno\nI built a simple HTTP server, running in a Docker container, that reads from a\nmounted folder called <code>files</code>. The endpoint <code>/list</code> will return each folder name\nthat exists in the main folder. The endpoint <code>/album/:albumName</code> will list all\nthe images in the given album folder folder. And finally the endpoint\n<code>/photo/:albumName/:photoName</code> returns the image.</p>\n<p>Look at the <a href=\"https://github.com/MrOggy85/photo-file-server\" rel=\"noopener noreferrer\">code</a></p>\n<h3 id=\"nextjs---show-photos\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#nextjs---show-photos\"></a>Next.js - Show Photos</h3><p>Next.js is a JS Framework on top of React and Node.js from Vercel. The website\nbasically have 2 routes. The index route will list the albums. When clicking on\nan album the page will list all the photos in the album.</p>\n<p>Look at the <a href=\"https://github.com/MrOggy85/photo-blog\" rel=\"noopener noreferrer\">code</a></p>\n<h2 id=\"further-automation\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#further-automation\"></a>Further Automation</h2><p>To make the deployment easy I am leveraging Vercel to host the frontend and to\nauto-deploy on each push to <code>master</code>.</p>\n<p>I also setup Docker Hub to listen for pushes to <code>master</code> and auto-publish a new\nDocker image. Currently this flow is semi-automated cause I still need to login\nto my server and pull the latest image and restart the container.</p>\n<h2 id=\"conclusion\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#conclusion\"></a>Conclusion</h2><p>That's it! If you follow these steps you now have a photo blog you can share\nwith your friends and super easily update with new albumbs and photos.</p>\n<p>You can find my photo blog here: <a href=\"https://www2.oskarlindgren.se/photos\" rel=\"noopener noreferrer\">https://www2.oskarlindgren.se/photos</a></p>\n<p>Repos</p>\n<ul>\n<li><a href=\"https://github.com/MrOggy85/photo-file-server\" rel=\"noopener noreferrer\">Photo File Server repo</a></li>\n<li><a href=\"https://github.com/MrOggy85/photo-blog\" rel=\"noopener noreferrer\">Photo Blog repo</a></li>\n</ul>\n",
            "url": "https://okuno.se/blog/photo-blog-with-nextjs-deno-syncthing",
            "title": "Photo Blog with NextJS, Deno and Syncthing",
            "summary": "'How to create an auto-publishing Photo Blog, powered by Next.js, Deno and Syncthing'",
            "image": "https://okuno.se/blog/photo-blog-nextjs-deno-syncthing/Photo_Blog.png",
            "date_modified": "2021-05-16T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/self-hosting-running-your-own-services",
            "content_html": "<p>What put me on this journey is a couple of factors:</p>\n<ul>\n<li>Ownership/Control - owning the service (together with the community)</li>\n<li>Privacy - owning the data</li>\n<li>Learning - opportunity to learn</li>\n<li>Stability - no unexpected changes</li>\n<li>Reliability - the service will not dissapear (Google graveyard...)</li>\n<li>Cost - server cost but no service costs</li>\n</ul>\n<p>To host your own services is a really good learning opportunity for server\nmanagement and security. And as an added bonus you get personal services which\nis only used by you and your friends.</p>\n<h2 id=\"how-does-my-server-structure-look-like\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#how-does-my-server-structure-look-like\"></a>How does my server structure look like?</h2><p><img src=\"self-hosting-running-your-own-services/portainer_digitalocean_docker.png\" alt=\"Docker logo\" width=\"840\" height=\"387\" /></p>\n<p>I have two small virtual servers (1 CPU, 1GB RAM each) on\n<a href=\"https://www.digitalocean.com/\" rel=\"noopener noreferrer\">Digital Ocean</a> which costs me 15$/month. And I\nrun all my services with <a href=\"https://www.docker.com/\" rel=\"noopener noreferrer\">Docker</a> to make most use of\neach server. Docker also simplifies maintenance drastically since nothing has to\nbe directly installed on the server. And I use <a href=\"https://caddyserver.com/\" rel=\"noopener noreferrer\">Caddy</a>\nas a reverse proxy for easy HTTPS/SSL support and super easy configuration\ncompared to nginx. This is how my Caddyfile looks like:</p>\n<pre><code>oskarlindgren.se {\n    redir https://www.oskarlindgren.se{uri}\n}\n\nwww.oskarlindgren.se {\n    root * /usr/share/caddy\n\n    redir /blog /blog/\n    reverse_proxy /blog/* http://tech-blog:2368\n\n    redir /photos /photos/\n        reverse_proxy /photos/* http://photo-blog:2368\n\n    file_server\n}\n\nMASKED.oskarlindgren.se {\n    reverse_proxy http://owncloud-docker-server_owncloud_1:8080\n}\n\nMASKED.oskarlindgren.se {\n    reverse_proxy http://firefly:8080\n}\n\nMASKED.oskarlindgren.se {\n    reverse_proxy http://freshrss\n}\n\nMASKED.oskarlindgren.se {\n    reverse_proxy http://fathom:8080\n}</code></pre><p>I also recently added a 300GB volume for 30$/month for additional storage.\nPrimarily to be able to store photos.</p>\n<p>Total monthly cost: 45$</p>\n<p>This is my price for privacy and learning :)</p>\n<h2 id=\"what-services-do-i-run\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#what-services-do-i-run\"></a>What services do I run?</h2><h3 id=\"email---mailu\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#email---mailu\"></a>Email - <em>Mailu</em></h3><p>Running my own email server may be the most important service I have. The main\nreason is privacy. The reason you get email for free from Google and others is\nthat they are reading your emails and knows who you are communicating with. I\nfeel that it's a bit creepy. There are some services that offer you privacy, but\nyou have to trust them and you have to rely on their service. The ultimate\nsolution is to run your own email server. It's not super easy but there are\nmultiple projects that give you an easy setup. I use <a href=\"https://mailu.io/\" rel=\"noopener noreferrer\">Mailu</a>\nwhich is a stable email solution.</p>\n<p>Update: Even though I ran Mailu without any bigger problems for a while I found\nit too tedious to tinker with. Especially for sending email. I now switched to\n<a href=\"https://tutanota.com/\" rel=\"noopener noreferrer\">Tutanota</a> which is a ery privacy focues email provider\nbased in Germany.</p>\n<p>I am still using my own domain, and it was easy to allow wild card aliases which\nallows me to create email aliases on the fly to have a unique address for each\nservice I signed up for.</p>\n<h3 id=\"calendar-contacts-documents---owncloud-nextcloud\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#calendar-contacts-documents---owncloud-nextcloud\"></a>Calendar, Contacts, Documents - <em>Owncloud Nextcloud</em></h3><p>Update: I recently switched from Owncloud to Nextcloud since Nextcloud comes\nwith batteries included for a personal use.</p>\n<p>It's super easy to run <a href=\"https://nextcloud.com/\" rel=\"noopener noreferrer\">Nextcloud</a> (or\n<a href=\"https://owncloud.com/\" rel=\"noopener noreferrer\">Owncloud</a>) and let your phone sync your contacts,\ncalendar and documents from it. And as an extra bonus you can store all your\nimportant documents there instead of in Google Drive or Dropbox.</p>\n<h3 id=\"blog---ghost-nuxt\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#blog---ghost-nuxt\"></a>Blog - Ghost Nuxt</h3><p>This blog is running on Ghost and hosted on my server Vercel powered by Nuxt.\nThe analytics is provided by <a href=\"https://www.fathomhq.com/\" rel=\"noopener noreferrer\">Fathom</a> which has a\nLite version you can use for free.</p>\n<p>Update: I decided to use a static site generator instead of Ghost. Ghost is well\nsuited for someone with low technical knowledge, but when you are a developer it\nbecomes a blocker rather than an enabler. I also don't like the fact that it\nrelies on a DB. I want something super fast and simple. I got inspired by\n<a href=\"https://derkinzi.de/nuxt-jam-stack/\" rel=\"noopener noreferrer\">this article from derkizni</a> and set up\n<a href=\"https://nuxtjs.org/blog/creating-blog-with-nuxt-content\" rel=\"noopener noreferrer\">Nuxt</a> using\n<a href=\"https://content.nuxtjs.org/\" rel=\"noopener noreferrer\">@nuxt/content</a>.</p>\n<h2 id=\"homepage---caddy\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#homepage---caddy\"></a>Homepage - <em>Caddy</em></h2><p><img src=\"oskarlindgren_homepage.png\" alt=\"Screenshot of Homepage\" width=\"984\" height=\"691\" /></p>\n<figcaption>www.oskarlindgren.se</figcaption>\n\n<p>My homepage is just a HTML file and a CSS file. I tried to keep it light, which\nmakes it eligible for the <a href=\"https://250kb.club/\" rel=\"noopener noreferrer\">250kb club</a>. Using Caddy to\nserve these static files with TLS/SSL support makes it super easy to make\nchanges through sFTP.</p>\n<p>Update: I'm still using Caddy as a reverse proxy for all my self hosted\nservices. But, I switched to Vercel for my homepage and other frontend because\nof the ease of deployment.</p>\n<h3 id=\"photos---photoprism\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#photos---photoprism\"></a>Photos - <em>PhotoPrism</em></h3><p>I recently started running <a href=\"https://photoprism.app/\" rel=\"noopener noreferrer\">PhotoPrism</a> to organize and\nhost my photos. It's an alternative to Google Photos and Dropbox which can auto\nimport your photos and organize them based on Tensorflow and analyzing the image\nmetadata.</p>\n<p>However, PhotoPrism needs at least 2 CPU cores and is very CPU intensive when\ningesting photos so I decided to put it on my local server at home. I tried some\nlighter services like Piwigo and Lychee, but they where a bit too light when it\ncomes to the features I was looking for.</p>\n<h3 id=\"rss-reader---freshrss\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#rss-reader---freshrss\"></a>RSS Reader - <em>FreshRSS</em></h3><p>To consume blogs, newsletters, etc I use FreshRSS which is a very simple and\nlight service to keep track of the blogs I like to read. Instead of relying on\nan external service like Feedly you can just run your own.</p>\n<h3 id=\"notes-files-and-passwords---syncthing--keepass\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#notes-files-and-passwords---syncthing--keepass\"></a>Notes, Files and Passwords - <em>Syncthing / KeePass</em></h3><p>I have opted out of any central storage of my files by using\n<a href=\"https://syncthing.net/\" rel=\"noopener noreferrer\">Syncthing</a>. Syncthing let's you sync your files\ndirectly to any other device (Note: It doesn't work on iPhone). This is very\nconvenient to:</p>\n<ul>\n<li>backup your mobile photos to your server</li>\n<li>sync documents between phone and laptop</li>\n<li>sync your keepass password file</li>\n<li>sync your ssh keys</li>\n</ul>\n<p>For my notes I just have a folder with markdown files (which I view/edit with\nVSCode). Then I can use <a href=\"https://gsantner.net/project/markor.html\" rel=\"noopener noreferrer\">Markor</a> on my\nAndroid phone to easily view and edit my notes.</p>\n<p>Previously I used Lastpass, but I switched to <a href=\"https://keepass.info/\" rel=\"noopener noreferrer\">KeePass</a>\nsince I don't want to store my passwords with someone else. With KeePass and\nSyncthing you can achieve the same level of convenience as with Lastpass, but\nwithout the risk of your passwords getting leaked in a hack of Lastpass.</p>\n<h2 id=\"why-do-you-want-to-run-your-own-services\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#why-do-you-want-to-run-your-own-services\"></a>Why do you want to run your own services?</h2><p>There are a couple of reasons why you want to run your own services.</p>\n<h3 id=\"privacy\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#privacy\"></a>Privacy</h3><blockquote>\n<p><em>You wouldn't give your front door key to your local store owner, right?</em></p>\n</blockquote>\n<p>When you are giving your data to someone else, they will use it for other\npurposes than yours. It's hard to predict what someone else can find out about\nyou or what they will do to the data once they have it. Normally a service is\nrun to make money for the owner. This means that they will use your data to\nmaximize their profit. If the service chooses to try to improve the service\nbased on your data that could be a net benefit for both of you. However, the\nservice providers purpose is to make you stay, not to help you. They may try to\ngame you into staying or even create a wall garden. If you keep your data to\nyourself and only share necessary data to specific people you know you will have\na safer life.</p>\n<p>Real world example: You wouldn't give your front door key to your local store\nowner, right? Even if it would be convenient if they can go in and leave some\ngroceries in your kitchen, it wouldn't feel safe to let in a stranger in your\nhome unsupervised...</p>\n<h3 id=\"ownership\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#ownership\"></a>Ownership</h3><p>It is very convenient to rely on a service to store and organize your data. But,\nwhat happens when the service goes down, or when they decide to shut down\ncompletely. When you run your own service you run it for yourself. The purpose\nis to help yourself to organize your life. You will probably not create any of\nthese solutions from scratch, but rather rely on Open Source and Free Software.\nThese projects are driver by likeminded people that want to solve a problem and\nto let others use it for free.</p>\n<p>Of course you have now traded a dependency of a for-profit to a dependency on an\nopen source service. But, for me I think it's better to be dependent on a free,\nopen source, right to fork service than a paid, proprietary, closed source\nservice.</p>\n<h3 id=\"security\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#security\"></a>Security</h3><blockquote>\n<p><em>The probability that you will be a target of a hack is lower than any known\nservice out there.</em></p>\n</blockquote>\n<p>When you run a server that is accessible on the internet you will constantly be\nproned by script kiddies. Just make sure you have\n<a href=\"https://www.fail2ban.org/wiki/index.php/Main_Page\" rel=\"noopener noreferrer\">Fail2ban</a> setup and that you\ndon't expose any ports that you don't need to limit attack vectors. The\nprobability that you will be a target of a hack is lower than any known service\nout there. However the top services out there will probably be way better than\nyou at server security. In the end, as long as you are comfortable with the risk\nwhen running your own services and take proper precautions it should be fine.</p>\n<h3 id=\"backup\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#backup\"></a>Backup</h3><p>Backup, backup, backup. Even if you rely on a service you should make sure to\nbackup your data regularly. However, when you run your own it's even more\nimportant to have backups. Make sure you store the backups in another machine.</p>\n",
            "url": "https://okuno.se/blog/self-hosting-running-your-own-services",
            "title": "Self-hosting - running your own services",
            "summary": "'Getting rid of Google, Dropbox, Microsoft and others.'",
            "image": "https://okuno.se/blog/self-hosting-running-your-own-services/pexels-brett-sayles-2881229.jpg",
            "date_modified": "2020-12-31T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/making-a-deno-cli-learning-japanese-numbers",
            "content_html": "<p>I have been a heavy node user and advocate it for several years now. It's a\nreally empowering tool for frontend devs like me. Node is still really good and\nthere is no immediate reason for switching to Deno if you don't see the problems\nas presented by the creator, Ryan Dahl:</p>\n<div>\n<div>\n\n\n</div>\n</div>\n\n<blockquote>\n<p><em>“With Deno you get TypeScript as a first class citizen”</em></p>\n</blockquote>\n<p>Since around 1,5 years ago I have been mainly writing code using TypeScript. I\nwas hesitant at first since I didn't think types was needed in Javascript until\nI started using TypeScript. It really makes writing Javascript Apps a bliss.\nWith Deno you get TypeScript as a first class citizen. No need for any babel or\ntypescript compiler setup. Deno will natively support your code. If you also\nlike typescript and don't like extra setup boilerplate code Deno is for you.</p>\n<blockquote>\n<p><em>“Deno allows you to get your dependencies from anywhere on the web”</em></p>\n</blockquote>\n<p>Another reason for using Deno is more of a political one. NPM has grown and are\nnow part of Microsoft. It is a centralized resource which is a part of the\ncentralization of the web we have seen in the recent years. Sure, you can still\nhost your own NPM repository, but by design Node needs to be compatible with\nNPM. Deno allows you to get your dependencies from anywhere on the web. If that\nis from Github or somewhere else it's totally up to the you and the community.\nTo be fair, currently most of the libraries are hosted in Github or NPM.</p>\n<blockquote>\n<p><em>“Talk is cheap. Show me the code.” - Linus Torvalds</em></p>\n</blockquote>\n<p>That's all about the background of why I like Deno. Let's move on to the code. I\nwanted to make a simple app where a number was spoken in Japanese and the user\nwould guess the number by typing it directly. Writing a simple script executed\nin the terminal makes the app very light with no need to create a Web interface\nor Desktop application. But, I still wanted the TUI to be nice and snappy so\nthat the user can go through a lot of numbers fast.</p>\n<p>I chose to go with a REPL approach to make the program continue executing until\nthe user chooses to exit (by ctrl-c). The core of the program is this while\nloop:</p>\n<pre><code>const buffer = new Uint8Array(1024);\n\nwhile (true) {\n  const input = await Deno.stdin.read(buffer);\n  const textRaw = new TextDecoder().decode(buffer.subarray(0, input!));\n  const text = textRaw.trim();\n  console.log(`you wrote: ${text}`);\n}</code></pre><p>Btw, did I mention that Deno supports top-level async/await?</p>\n<p>As you can see in the code above. We are simply (a)waiting for the user to input\nsome text. Then we can decode it and do something with that text. For this app\nwe want to check if the users guess was the expected number. Let's add some more\ncode!</p>\n<pre><code>const nextNumber = Math.round(Math.random() * maxNumber);\n\nconst input = await Deno.stdin.read(buffer);\nconst textRaw = new TextDecoder().decode(buffer.subarray(0, input!));\nconst text = textRaw.trim();\n\nconst numberGuess = Number(text);\nconst result = numberGuess === nextNumber;\n\nif (result) {\n  console.log(\"You are correct!\");\n} else {\n  console.log(`Incorrect! The correct number is: ${nextNumber}`);\n}</code></pre><p>This will let the user input a guess and then we check against <code>nextNumber</code>.\nHowever as you can see there is an undeclared variable here: <code>maxNumber</code>. This\nis an input that the user has to give when starting the program to determine\nwhat range you want to practice on. Deno makes it easy to read the parameters of\na program:</p>\n<pre><code>const maxNumberRaw = Deno.args[0];\nconst maxNumber = Number(maxNumberRaw);\nif (Number.isNaN(maxNumber)) {\n  console.log(\"Please start program like this:\");\n  console.log(\"./start.sh MAX_NUMBER\");\n  Deno.exit(0);\n}</code></pre><p>But, the user is still not hearing any number! Ok, let's add one of the main\npieces of the program; Let the machine speak! So, this next part assumes that\nthe user is running a Mac. This can easily be modified to be adapted to Linux\nand Windows. I am using the program say to do text-to-speech. This comes\npreinstalled on any Mac. Then we can use the Japanese voice to let the number be\nspoken in Japanese. To run a command from Deno it's as easy as this:</p>\n<pre><code>function sayNumber(number: string) {\n  Deno.run({\n    cmd: [\"say\", \"-v\", \"Kyoko\", number],\n  });\n}</code></pre><p>Each string in the array maps to a word written in the terminal. Just be careful\nsince it's async. If you want to make sure that the command you are executing is\nfinished before moving on you need to await it.</p>\n<p>Now we can add use this function to let the computer speak the number and the\nuser can guess it. I also added a clear command after each guess to clean up for\nthe next number. And of course some cute bunnies to emote the result to the\nuser.</p>\n<pre><code>//   /) /)     (\\\\_/)   (\\\\-/)\n//  ( ^.^ )    (&gt;.&lt;)    (='.'=)\n// C(\") (\")   (\")_(\")   (\")-(\")o</code></pre><p>See the full source <a href=\"https://github.com/MrOggy85/japanese-numbers\" rel=\"noopener noreferrer\">here</a>.</p>\n<p>Happy coding!</p>\n",
            "url": "https://okuno.se/blog/making-a-deno-cli-learning-japanese-numbers",
            "title": "Making a Deno CLI - Learning Japanese Numbers",
            "summary": "'I have been following the evolution of Deno since I first heard about it. I really like node.js and I was playing around with it when it since it was released. '",
            "image": "https://okuno.se/blog/japanese_numbers_deno.jpg",
            "date_modified": "2020-07-27T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/where-does-docker-names-come-from",
            "content_html": "<p>The random name generation comes from this package in the moby repo. There is a\nlist of adjectives and a list of famous scientists and hackers that gets\nrandomly combined to form the names of the docker containers we all have come to\nlove. The combinations are random with one exception of <code>boring_wozniak</code> since\n<a href=\"https://github.com/moby/moby/blob/master/pkg/namesgenerator/names-generator.go#L844\" rel=\"noopener noreferrer\"><em>\"Steve Wozniak is not boring\"</em></a>\naccording to the comment.</p>\n<p>The list is actively maintained and new scientists are added and also removed.\nAfter being accused of sexually abusing trafficking victims, Marvin Minsky was\nremoved along with Richard Stallman that publicly defended his actions.</p>\n<ul>\n<li>related article:\n<a href=\"https://frightanic.com/computers/docker-default-container-names/\" rel=\"noopener noreferrer\">https://frightanic.com/computers/docker-default-container-names/</a></li>\n<li>Image credit:\n<a href=\"https://www.praqma.com/stories/Deploying-to-kubernetes-as-part-of-a-pipeline/\" rel=\"noopener noreferrer\">https://www.praqma.com/stories/Deploying-to-kubernetes-as-part-of-a-pipeline/</a></li>\n</ul>\n",
            "url": "https://okuno.se/blog/where-does-docker-names-come-from",
            "title": "Where does \"hardcore_einstein\" come from?",
            "summary": "'Did you ever wonder about how those weird names for docker containers were generated? Probability not, but the more you know!'",
            "image": "https://okuno.se/blog/docker_funny.jpg",
            "date_modified": "2019-10-04T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/evolution-of-frontend-devs",
            "content_html": "<p>I recently listened to the podcast Shoptalk episode\n<a href=\"https://shoptalkshow.com/episodes/346/\" rel=\"noopener noreferrer\">\"Is there a Great Divide\"</a> about the\ndivide between frontend developers. I was not aware that there was a divide in\nthe first place. The main symptom they discuss is the difference between what a\njob listing says and what the actual job is.</p>\n<p>This inspired me to think about where frontend development is today. I have\nlately also recently seen a divide, but on another topic, when it comes to use\nstatic types and Object Oriented Programming in the frontend. But, let's go back\nfrom the start first to see why we are where we are today.</p>\n<p>Disclaimer:<br /> This is a gross simplification of the history of the web and\ndata has been cherry picked to fit my narrative. There is probably some facts\nnot mentioned or wrongly described that effects some of my points and I am open\nto feedback to adjust my views.</p>\n<h2 id=\"first-gen---world-wide-web-dev\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#first-gen---world-wide-web-dev\"></a>First Gen - World Wide Web Dev</h2><p><img src=\"evolution-of-frontend-devs/9407011_31-A5-at-72-dpi.jpg\" alt=\"Sir Timothy Berners-Lee in front of a computer\" width=\"561\" height=\"360\" /></p>\n<figcaption>source: [Cern](https://cds.cern.ch/record/39437#31)</figcaption>\n\n<p>The information Highway was born at CERN by Tim Beerners-Lee and\n<a href=\"https://en.wikipedia.org/wiki/World_Wide_Web\" rel=\"noopener noreferrer\">released to the public in 1991</a>.\nAt this point every website was just static webpages directly served from the\nweb servers. Each URL pointed to a specific directory which had an index.html\nwhere you also could point to other sources like css files, images, etc.\nUpdating the website meant changing the html file, uploading a new version by\nftp and then the users got the new content. After some time the need for a more\ndynamic experience led to the introduction of\n<a href=\"https://en.wikipedia.org/wiki/JavaScript#History\" rel=\"noopener noreferrer\">Javascript in 1995 by Brendan Eich</a>\nand during the same year\n<a href=\"https://en.wikipedia.org/wiki/HTTP_cookie#History\" rel=\"noopener noreferrer\">cookies</a> were introduced by\nthe same state of the art browser at the time: Netscape.</p>\n<p>At this point frontend development was not really a known term. Programmers were\nable to create html files that could be interpreted by a browser to show a\ndocument. It was mainly used in research and websites where very informative and\nnon interactive.</p>\n<h2 id=\"second-gen---web-20\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#second-gen---web-20\"></a>Second Gen - Web 2.0</h2><p><img src=\"evolution-of-frontend-devs/gilfoyle.jpg\" alt=\"Gilfoyle from Silicon Valley TV Show\" width=\"770\" height=\"434\" /></p>\n<figcaption>source: <a href=\"https://www.techrepublic.com/pictures/our-21-favorite-fictional-techies-from-tv-and-movies/\">techrepublic.com</a></figcaption>\n\n<blockquote>\n<p><em>\"I tried to make it slow. I really did. But I'm not Dinesh. It's very\ndifficult for me to do shitty work.\" - Gilfoyle</em></p>\n</blockquote>\n<p>The need for more an even more dynamic experience led\n<a href=\"https://en.wikipedia.org/wiki/PHP#History\" rel=\"noopener noreferrer\">Rasmus Lerdorf to, in 1995, create PHP</a>\nto be able to serve web pages through a template engine. This led to a\nremarkable creativity boost for web development since you could access a\ndatabase to tailor the experience of the user and create an html page on the\nfly.</p>\n<p>But the most important interesting part of this time was that a lot of people\ncould set up their own website easily thanks to services like e.g. Angelfire,\nGeocities, AOL, etc. I think that, the idea that frontend is easy and\nunimportant was born during this era. The second half of the 90s can be\nsummarized by these gifs that frequently was shown on peoples websites:</p>\n<p><img src=\"evolution-of-frontend-devs/HoHollywoodPicture3800construction.gif\" alt=\"Under Contruction GIF\" width=\"467\" height=\"30\" />\n<img src=\"evolution-of-frontend-devs/ajaj0077lines_bulletsconstruction.gif\" alt=\"Under Contruction GIF\" width=\"573\" height=\"18\" />\n<img src=\"evolution-of-frontend-devs/AtAthens6321underconstruction_lemmings.gif\" alt=\"Under Contruction GIF\" width=\"600\" height=\"32\" /></p>\n<figcaption>source: http://textfiles.com/underconstruction/</figcaption>\n\n<p><img src=\"evolution-of-frontend-devs/firebar.gif\" alt=\"Firebar GIF\" width=\"600\" height=\"50\" /></p>\n<figcaption>source: <a href=\"https://web.archive.org/web/20090807121959/http://geocities.com/curt_sigurdsen/ALBUM01.htm)\">Internet Archive of Curt Sigurdsen's Geocities Webpage</a></figcaption>\n\n<p>This technology would however enable the environment where internet based\nbusiness could start to form. And it also led to a specialization between\nfrontend and backend. The frontend developers was mainly focusing on the design\nof the page through html and css with a small portion of Javascript to enable\nsome dynamic features. And the backend developers would focus on the business\nlogic of the site.</p>\n<p><img src=\"evolution-of-frontend-devs/bubble_burst.jpg\" alt=\"WWW bubble burst\" width=\"640\" height=\"340\" /></p>\n<figcaption>source: <a href=\"https://betanews.com/wp-content/uploads/2014/10/bubble_burst.jpg\">betanews.com</a></figcaption>\n\n<p>The web gained a lot of traction during the second half of the 90s and got a\ncrazy hype which led to the dot-com bubble which\n<a href=\"https://en.wikipedia.org/wiki/Dot-com_bubble#Bursting_of_the_bubble\" rel=\"noopener noreferrer\">bursted in 2000</a>.\nBut, the web was not dead. The technology was still great but the expectations\nhad to be realigned closer to the reality of what promises the technology and\ndevelopers could offer.</p>\n<p>During the first decade of 2000 the web became even more dynamic and the term\nweb 2.0 was coined and got popularized by\n<a href=\"https://en.wikipedia.org/wiki/Web_2.0#Web_2.0\" rel=\"noopener noreferrer\">O'Rilley Media in 2004</a>. The web\nbecame a platform for applications and the clients (browsers) started to become\nricher. Realtime updates of the website became a big feature.</p>\n<p>Now frontend development became more complex. A lot of business logic that was\npreviously handled by the server was moved to client side to enable user\ninteraction without the need of a page reload with the use of Ajax calls. There\nwas also a ton of third party integrations that bloated the global scope and the\ncss files grew into biblical proportions. And there were still a lot of\ndifferences between browsers which led to\n<a href=\"https://en.wikipedia.org/wiki/JQuery#History\" rel=\"noopener noreferrer\">jQuery being born in 2006 by John Resig</a>.</p>\n<p><img src=\"evolution-of-frontend-devs/spaghetti.jpeg\" alt=\"man with spaghetti\" width=\"800\" height=\"1200\" /></p>\n<figcaption>sauce: <a href=\"https://medium.com/@orsararecipes/garlic-shrimp-with-linguine-recipe-160a2ab633ac\">Pasquale Sciarappa</a></figcaption>\n\n<blockquote>\n<p><em>Leave the gun – take the cannoli. - Clemenza</em></p>\n</blockquote>\n<p>By around 2010 web development was a mess, imho. The server and client code was\na perfect spaghetti carbonara served a million times a second around the globe.\nA lot of great developers and organizations started to generalize the solutions\nthey created and shared them with the community which led to the next generation\nof frontend developers.</p>\n<h2 id=\"third-gen---javascript-dev\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#third-gen---javascript-dev\"></a>Third Gen - Javascript Dev</h2><p><img src=\"evolution-of-frontend-devs/optimize.jpeg\" alt=\"hipster boy with mac\" width=\"684\" height=\"1024\" /></p>\n<figcaption>source: <a href=\"https://www.wwdjapan.com/24925\">WWD Japan, Photo by Kuba Dabrowski</a></figcaption>\n\n<blockquote>\n<p><em>\"...which made frontend development into it's own domain.\"</em></p>\n</blockquote>\n<p>In 2010 AngularJS and Backbone was released, followed by Ember the next year.\nThis was the start of Web Apps which introduced concepts like MVC which were\nnever used in frontend before. It was also a step back to the first generation\nof websites with statically served websites, but a leap forward for dynamic\nwebsites and rich clients. The result was a totally decoupled frontend and\nbackend which made frontend development into it's own domain. The server didn't\nneed to serve html and became an Restful API for the frontend to consume\n(However the need for a good SEO score later reversed this decision and\nintroduced server side rendering of the frontend app).</p>\n<blockquote>\n<p><em>\"This would later be known as Javascript fatigue to try to keep up with all\nthe new stuff.\"</em></p>\n</blockquote>\n<p>When the frontend was living in it's own world outside the server a lot of\ntooling was invented to handle the need for a rapid development which led to\nGrunt, Gulp and Webpack to be introduced. Furthermore NPM became the place to\nshare libraries, where several new solutions would be published every day. This\nwould later be known as Javascript fatigue to try to keep up with all the new\nstuff. Javascript as a language grew with a lot of new features. But, all\nfeatures where not supported by all browsers or not yet finalized which led to\nthe creation of Babel. As a frontend developer in around 2016 you where expected\nto master these tools and to at least have the knowledge of one of the\njavascript frameworks out there.</p>\n<p>This new frontend development divided the notion of a frontend developer from\nthe previous era. Being proficient in html and css was not enough to get you\nhired anymore. Now programming skills and knowledge about certain frameworks was\nwhat frontend development was all about.</p>\n<p>The shift didn't happen over night though. Most companies continued to use their\nprevious server side template based solutions, but slowly started to convert to\nthe new javascript frameworks as they matured. As more and more businesses\nshifted their tech stack to heavily rely on a Javascript framework, this meant\nthat more and more of their business logic moved to the frontend.</p>\n<blockquote>\n<p><em>\"...the biggest migration wave in programming history.\"</em></p>\n</blockquote>\n<p>This is what I identify as the biggest migration wave in programming history.\nNot only a shift in tech stack, but especially for backend developers migrating\nto a brand new world. Backend developers that previously only been working on\nserver side code in statically typed compiled languages now found themselves in\nthe dynamic and browser interpreted domain of Javascript. And they were far from\nhappy to find themselves in this country... As Javascript gained traction and\npopularity so did the frustration which was popularized by the\n<a href=\"https://www.destroyallsoftware.com/talks/wat\" rel=\"noopener noreferrer\">wat presentation in 2012 by Gary Bernhardt</a>.\nIt's an understatement to say that Javascript as a language did not fit into the\nexpectations of these developers.</p>\n<h2 id=\"predicted-forth-gen---typescript-dev\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#predicted-forth-gen---typescript-dev\"></a>(predicted) Forth Gen - Typescript Dev</h2><p><img src=\"evolution-of-frontend-devs/harvey.jpg\" alt=\"Harvey from Suits TV Show\" width=\"880\" height=\"495\" /></p>\n<figcaption>source: <a href=\"https://dave.uktv.co.uk/suits/article/dave-calls-harvey-qa/\">UKTV</a></figcaption>\n\n<p>There where several attempts to radically improve Javascript with a superset\nlanguage such as\n<a href=\"https://en.wikipedia.org/wiki/CoffeeScript\" rel=\"noopener noreferrer\">CoffeeScript, introduced in 2010 by Jeremy Ashkenas</a>,\nfollowed by\n<a href=\"https://en.wikipedia.org/wiki/Dart_(programming_language)#History\" rel=\"noopener noreferrer\">Dart in 2011</a>\nand\n<a href=\"https://en.wikipedia.org/wiki/Elm_(programming_language)#History\" rel=\"noopener noreferrer\">Elm in 2012</a>.\nThese languages were popular, but never gained any mainstream traction.</p>\n<p>Class was introduced in ECMAScript 2015 to the delight of objected oriented\ndevelopers, but was imho, vastly misinterpreted since it was only a syntactic\nsugar for using the new operator. This however created a big shift in how a lot\nof Javascript was written, since the former backend developers were used to\ndeclare classes when coding. There was already an awkward naming convention in\nJavascript to use an underscore to mark a method as \"private\", when in reality\nit was accessible publicly. This remained a practice even after class was\nintroduced, since it didn't actually change the underlying language.</p>\n<p>There was however still a big annoyance with Javascript: The lack of static\ntypes. Enter: TypeScript by Microsoft. The first version of\n<a href=\"https://en.wikipedia.org/wiki/TypeScript#History\" rel=\"noopener noreferrer\">TypeScript was released 2012</a>,\nbut it wasn't until after ECMAScript 2015 and\n<a href=\"https://en.wikipedia.org/wiki/Angular_(web_framework)#Version_2\" rel=\"noopener noreferrer\">Angular 2 which was officially released in 2016</a>,\nuntil it gained some serious traction of the community. Typescript became the\nstandard way of writing Angular apps. Facebook had their own way of doing static\ntype checking in React with Flow, but it never went mainstream and now a lot of\ndevelopment is now done with Typescript.\n<a href=\"https://vuejs.org/v2/guide/typescript.html\" rel=\"noopener noreferrer\">Vue added Typescript support from version 2</a>\nand in their next version will be\n<a href=\"https://medium.com/the-vue-point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf\" rel=\"noopener noreferrer\">written in Typescript and be a first class citizen</a>.\nThe Javascript community embraced Typescript during 2018 and more and more\nlibraries bundled type definitions and wrote their libraries using Typescript.</p>\n<h2 id=\"the-future-of-frontend-development\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#the-future-of-frontend-development\"></a>The Future of Frontend Development</h2><p>The web browser is today a synonym to Chrome. The\n<a href=\"https://en.wikipedia.org/wiki/Browser_wars\" rel=\"noopener noreferrer\">Browser Wars</a> are over and Chromium\nis the victor. Microsoft's decision to shift Edge to be Chromium based is a\nproof that the game has changed. The only odd sheep left on the battlefield is\nFirefox and Safari, but they can't currently put up a fight.</p>\n<p>Google had plans to introduce Dart as a replacement for Javascript in the\nbrowser, but\n<a href=\"https://techcrunch.com/2015/03/25/google-will-not-integrate-its-dart-programming-language-into-chrome/\" rel=\"noopener noreferrer\">cancelled those plans in 2015</a>.\nDuring this time Dart was not by any means a popular language and Chrome was\nstill not the dominant browser as it is today. But, today the environment has\nchanged. All the ingredients that wasn't present before is now in place. I\npredict that during 2019 there will be a first version of Edge and Chrome that\nsupports Typescript native in the browser.</p>\n<p>Being a frontend developer in the 2020s will be indistinguishable from a backend\ndeveloper. The wild wild west of frontend development has an expiration date\nwhich will come sooner than we expect. Be prepared for the change.</p>\n<p>Sources:</p>\n<ul>\n<li><a href=\"https://shoptalkshow.com/episodes/346/\" rel=\"noopener noreferrer\">https://shoptalkshow.com/episodes/346/</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/World_Wide_Web\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/World_Wide_Web</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/JavaScript#History\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/JavaScript#History</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/HTTP_cookie#History\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/HTTP_cookie#History</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/PHP#History\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/PHP#History</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/Dot-com_bubble#Bursting_of_the_bubble\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/Dot-com_bubble#Bursting_of_the_bubble</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/Web_2.0#Web_2.0\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/Web_2.0#Web_2.0</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/JQuery#History\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/JQuery#History</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/AngularJS\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/AngularJS</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/Backbone.js\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/Backbone.js</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/Ember.js#History\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/Ember.js#History</a></li>\n<li><a href=\"https://www.destroyallsoftware.com/talks/wat\" rel=\"noopener noreferrer\">https://www.destroyallsoftware.com/talks/wat</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/CoffeeScript\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/CoffeeScript</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/Dart_(programming_language)#History\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/Dart_(programming_language)#History</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/Elm_(programming_language)#History\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/Elm_(programming_language)#History</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/TypeScript#History\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/TypeScript#History</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/Angular_(web_framework)#Version_2\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/Angular_(web_framework)#Version_2</a></li>\n<li><a href=\"https://techcrunch.com/2015/03/25/google-will-not-integrate-its-dart-programming-language-into-chrome/\" rel=\"noopener noreferrer\">https://techcrunch.com/2015/03/25/google-will-not-integrate-its-dart-programming-language-into-chrome/</a></li>\n</ul>\n",
            "url": "https://okuno.se/blog/evolution-of-frontend-devs",
            "title": "Evolution of Frontend Devs",
            "summary": "\"Today the frontend scene is very broad and consist of a range of skills. That means if your main skills are creating a good design with CSS, it doesn't necessarily mean you also can configure webpack to handle Sass files.\"",
            "image": "https://okuno.se/blog/evolution-of-frontend-devs/harvey.jpg",
            "date_modified": "2019-04-01T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/private-npm-registry-with-verdaccio-2-2",
            "content_html": "<p>Before moving on. If you don't want to host your own NPM Registry you can just\nuse NPM's own solution for 7$ per user: <a href=\"https://www.npm-enterprise.com/\" rel=\"noopener noreferrer\">https://www.npm-enterprise.com/</a></p>\n<p>Still interested? Let's start!</p>\n<p>Note: This setup is written for:</p>\n<pre><code>Ubuntu 18.04\nDocker version 18.09.0, build 4d60db4\ndocker-compose version 1.21.2, build a133471</code></pre><p>It will probably be compatible with Mac and other Linux distros though.</p>\n<p>We will use docker and docker-compose. If you haven't installed them already\nplease download and install them here: <a href=\"https://www.docker.com/get-started\" rel=\"noopener noreferrer\">https://www.docker.com/get-started</a></p>\n<h2 id=\"what-is-a-npm-registry\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#what-is-a-npm-registry\"></a>What is a NPM Registry?</h2><p>It actually turns out that NPM has a pretty simple structure. You can just roll\nyour own with a Node.js server and a CouchDB instance and follow the\ninstructions from NPM: <a href=\"https://docs.npmjs.com/misc/registry\" rel=\"noopener noreferrer\">https://docs.npmjs.com/misc/registry</a></p>\n<h2 id=\"a-better-alternative-verdaccio\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#a-better-alternative-verdaccio\"></a>A better alternative: Verdaccio</h2><p>But, who wants to reinvent the wheel if we already have a Ferrari available with\nminimal setup? Let me introduce you to Verdaccio; \"a lightweight private npm\nproxy registry\" which is a pretty damn good description. It's a node.js server\nwith a simple file based db, but you can also configure it to store your\npackages in a Amazon S3 bucket or Google Cloud Storage.</p>\n<h2 id=\"verdaccio-with-docker-compose\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#verdaccio-with-docker-compose\"></a>Verdaccio with Docker-Compose</h2><p>We want to run Verdaccio as a docker container for ease of deployment. You can\nfind the latest Dockerfile here. We will use Verdaccio version 3, which is the\nlatest as of writing this blog post. First of, let's create a docker-compose.yml\nfile and fill it with this content:</p>\n<pre><code>version: '3.1'\n\nservices:\n  verdaccio:\n    container_name: 'verdaccio'\n    image: verdaccio/verdaccio:3.0.0\n    restart: always\n    ports:\n      - \"4873:4873\"\n    volumes:\n      - ./data:/verdaccio/\n    networks:\n      - docker-network\n\nnetworks:\n  docker-network:\ndriver: bridge</code></pre><p>Note that we are mounting a volume called \"data\". This is where Verdaccio stores\npackages and users. We want to store this on the host to be able to persist all\ndata when restarting our docker container. Let's create the data folder.</p>\n<pre><code>~$ mkdir data</code></pre><p>Verdaccio is running inside the Docker Container with it's own user <code>verdaccio</code>\nand usergroup <code>verdaccio</code>. You need to give permission to your System's\n<strong>Network Management</strong> service to make changes in the folder. Let's create the\ndata folder and set the correct permissions.</p>\n<pre><code>~$ sudo chown -R systemd-network data/</code></pre><p>That's it! Let's fire up this bad boy!</p>\n<pre><code>~$ docker-compose up -d</code></pre><p>You can now access it on <code>http://localhost:4873</code></p>\n<h2 id=\"configuring-verdaccio\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#configuring-verdaccio\"></a>Configuring Verdaccio</h2><p>The config file <code>data/conf/config.yaml</code> holds the configuration.</p>\n<p>Docs: <a href=\"https://verdaccio.org/docs/en/configuration\" rel=\"noopener noreferrer\">https://verdaccio.org/docs/en/configuration</a></p>\n<h2 id=\"basic-usage\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#basic-usage\"></a>Basic Usage</h2><p>Point your npm cli to Verdaccio</p>\n<pre><code>~$ npm set registry http://localhost:4873</code></pre><p>Add yourself as a user</p>\n<pre><code>~$ npm adduser --registry http://localhost:4873</code></pre><p>Now you can use npm as usual but everything will be proxied through Verdaccio.\nHave fun coding!</p>\n<p>Full source at Github: <a href=\"https://github.com/MrOggy85/verdaccio-docker\" rel=\"noopener noreferrer\">https://github.com/MrOggy85/verdaccio-docker</a></p>\n",
            "url": "https://okuno.se/blog/private-npm-registry-with-verdaccio-2-2",
            "title": "Setup a Private NPM Registry with Verdaccio and Docker",
            "summary": "\"When collaborating on frontend application writing component libraries our companies doesn't always want to share that code with the community (boo!). Using package managers like npm and yarn is really powerful to handle our third party code. When working locally it's easy to use npm link or yarn link for ease of development. Then when we want to use our library with our application we need to provide a repo link with a tag and auth token (ugly and unsafe!). Let's do it proper by publishing our library to our own NPM registry instead! And also get an additional cache layer in case NPM is down...\"",
            "image": "https://okuno.se/blog/mi_scusi.png",
            "date_modified": "2019-01-14T00:00:00.000Z"
        },
        {
            "id": "https://okuno.se/blog/creating-a-8-bit-retro-cv-page",
            "content_html": "<p>One of my first games I ever played was Sonic. The old sonic games for Sega Mega\nDrive has aged well and are still really enjoyable. In that spirit I decided an\nafternoon to have a page that had a side scrolling background with some basic\ninformation. Basically just some links to LinkedIn and Github where my real CV\nis :)</p>\n<p>My initial inspiration for the page was when I stumbled upon\n<a href=\"https://nostalgic-css.github.io/NES.css/\" rel=\"noopener noreferrer\">NES.css</a> from reading the\n<a href=\"https://frontendfoc.us/issues/371\" rel=\"noopener noreferrer\">Frontend Focus newsletter</a>. It was so simple\nand elegant and really touch my childish self. I just had to use it for a\nproject. And why not use that style for a CV page?</p>\n<h2 id=\"create-a-profile-card\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#create-a-profile-card\"></a>Create a Profile Card</h2><p>Add the NES.css to your <code>&lt;head&gt;</code></p>\n<div class=\"highlight notranslate\"><pre><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>link</span> <span class=\"token attr-name\">href</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>https://unpkg.com/nes.css@0.0.2/css/nes.min.css<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">rel</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>stylesheet<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/&gt;</span></span></pre></div><p>The next step is to create a container with a title using the NES.css framework.</p>\n<div class=\"highlight notranslate\"><pre><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>section</span> <span class=\"token attr-name\">class</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>container with-title<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span> <span class=\"token attr-name\">class</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>title<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span>Oskar<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">&gt;</span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>section</span><span class=\"token punctuation\">&gt;</span></span></pre></div><p>Then I will add a row with my Github picture and a encouraging text to navigate\nsome of the sites where I actually have some content to show.</p>\n<div class=\"highlight notranslate\"><pre><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>section</span> <span class=\"token attr-name\">class</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>container with-title<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span> <span class=\"token attr-name\">class</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>title<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span>Oskar<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">&gt;</span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">class</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>row<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>img</span> <span class=\"token attr-name\">src</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>https://avatars0.githubusercontent.com/u/2677573?s=460&amp;v=4<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">alt</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>profile picture<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">width</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>100<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">height</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>100<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/&gt;</span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token\"><span class=\"token attr-name\">style</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span><span class=\"token\"><span class=\"token property\">margin-left</span><span class=\"token punctuation\">:</span>28px<span class=\"token punctuation\">;</span></span><span class=\"token punctuation\">\"</span></span></span><span class=\"token punctuation\">&gt;</span></span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">&gt;</span></span>\n              Hello!\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">&gt;</span></span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">&gt;</span></span>\n              Please check me out on Github and LinkedIn or follow me on Twitter.\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">&gt;</span></span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>section</span><span class=\"token punctuation\">&gt;</span></span>\n\n#styles.css\n.row {\n  display: flex;\n  flex-direction: row;\n}</pre></div><p>The next row will be a list of icons with links to the services I want the\nvisitors to navigate to</p>\n<div class=\"highlight notranslate\"><pre><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">class</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>row right-aligned<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span>\n     <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>https://github.com/MrOggy85<span class=\"token punctuation\">\"</span></span> <span class=\"token\"><span class=\"token attr-name\">style</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span><span class=\"token\"><span class=\"token property\">margin-right</span><span class=\"token punctuation\">:</span>5px</span><span class=\"token punctuation\">\"</span></span></span><span class=\"token punctuation\">&gt;</span></span>\n         <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>i</span> <span class=\"token attr-name\">class</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>icon github is-medium<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>i</span><span class=\"token punctuation\">&gt;</span></span>\n     <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">&gt;</span></span>\n     <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>https://twitter.com/oskarlindgren<span class=\"token punctuation\">\"</span></span> <span class=\"token\"><span class=\"token attr-name\">style</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span><span class=\"token\"><span class=\"token property\">margin-right</span><span class=\"token punctuation\">:</span>5px</span><span class=\"token punctuation\">\"</span></span></span><span class=\"token punctuation\">&gt;</span></span>\n         <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>i</span> <span class=\"token attr-name\">class</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>icon twitter is-medium<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>i</span><span class=\"token punctuation\">&gt;</span></span>\n     <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">&gt;</span></span>\n     <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>https://www.linkedin.com/in/oskarlindgren/<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span>\n         <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>img</span> <span class=\"token attr-name\">src</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>assets/linkedin.png<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">width</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>48<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">height</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>48<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/&gt;</span></span>\n     <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">&gt;</span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n\n#styles.css\n.right-aligned {\n  justify-content: flex-end;\n}</pre></div><p>The result:</p>\n<p><img src=\"cv_homepage_card.png\" alt=\"CV Homepage Card\" width=\"598\" height=\"233\" /></p>\n<figcaption>NES style!!!</figcaption>\n\n<p>Okay so that very informative and pretty cool. But, it's not very engaging. I\nneeded some kind of visually stunning effect in the same spirit. I needed a side\nscrolling background!</p>\n<h2 id=\"creating-a-3-layer-side-scrolling-background\"><a class=\"anchor\" aria-hidden=\"true\" tabindex=\"-1\" href=\"#creating-a-3-layer-side-scrolling-background\"></a>Creating a 3 layer Side Scrolling Background</h2><p>To create a good depth perception 3 level of backgrounds scrolling at different\nspeeds is good enough. And we can achieve this with only CSS.</p>\n<p>First the HTML</p>\n<div class=\"highlight notranslate\"><pre><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">class</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>wrapper<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">class</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>mountains<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">class</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>trees<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">class</span><span class=\"token\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>grass<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span></pre></div><p>For the background images I found some really nice pictures at a Fandom site\ncalled\n<a href=\"http://supermariobrothersx.wikia.com/wiki/SMB2_Backgrounds\" rel=\"noopener noreferrer\">Super Mario Bros X Wiki</a>.\nI needed to adapt the pictures a little bit since the image will repeat 3 times\nat the x axis to create the illusion of scrolling (if you have a really wide\npicture you don't need this).</p>\n<p>To accomplish the scrolling effect we will use <code>animation</code> and <code>@keyframes</code>.\nLet's start with the first layer which will be the mountains in the far back.</p>\n<div class=\"highlight notranslate\"><pre><span class=\"token\">.mountains</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property\">position</span><span class=\"token punctuation\">:</span>absolute<span class=\"token punctuation\">;</span>\n  <span class=\"token property\">top</span><span class=\"token punctuation\">:</span>0<span class=\"token punctuation\">;</span>\n  <span class=\"token property\">bottom</span><span class=\"token punctuation\">:</span>0<span class=\"token punctuation\">;</span>\n  <span class=\"token property\">left</span><span class=\"token punctuation\">:</span>0<span class=\"token punctuation\">;</span>\n  <span class=\"token property\">right</span><span class=\"token punctuation\">:</span>0<span class=\"token punctuation\">;</span>\n  <span class=\"token property\">background</span><span class=\"token punctuation\">:</span><span class=\"token\"><span class=\"token function\">url</span><span class=\"token punctuation\">(</span><span class=\"token string\">'assets/mountains.gif'</span><span class=\"token punctuation\">)</span></span><span class=\"token punctuation\">;</span>\n  <span class=\"token property\">background-repeat</span><span class=\"token punctuation\">:</span> repeat-x<span class=\"token punctuation\">;</span>\n  <span class=\"token property\">animation</span><span class=\"token punctuation\">:</span> mountains-slide 60s linear infinite<span class=\"token punctuation\">;</span>\n  <span class=\"token property\">width</span><span class=\"token punctuation\">:</span> 3072px<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token\"><span class=\"token\">@keyframes</span> mountains-slide</span><span class=\"token punctuation\">{</span>\n  <span class=\"token\">0%</span><span class=\"token punctuation\">{</span>\n    <span class=\"token property\">transform</span><span class=\"token punctuation\">:</span> <span class=\"token function\">translate3d</span><span class=\"token punctuation\">(</span>0<span class=\"token punctuation\">,</span> 0<span class=\"token punctuation\">,</span> 0<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token\">100%</span><span class=\"token punctuation\">{</span>\n    <span class=\"token property\">transform</span><span class=\"token punctuation\">:</span> <span class=\"token function\">translate3d</span><span class=\"token punctuation\">(</span>-1024px<span class=\"token punctuation\">,</span> 0<span class=\"token punctuation\">,</span> 0<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></pre></div><p>The <code>width</code> is 3 times the image width size and the <code>translate3d</code> property tells\nit to move to the left and repeat infinitely. That's all there is to it. Then\nyou can just add as much layers as you want, position them absolutely and\nexperiment with the animation speed to create different velocity to get that\ndepth perception we are trying to achieve here.</p>\n<p>The end result:</p>\n<p><img src=\"oskarlindgren_website.gif\" alt=\"website preview\" width=\"1030\" height=\"696\" /></p>\n<figcaption>Scrolling like a beauty</figcaption>\n\n<p>View the full source at <a href=\"https://github.com/MrOggy85/homepage\" rel=\"noopener noreferrer\">Github</a>.</p>\n<p>Checkout it out live at <a href=\"https://www.oskarlindgren.se\" rel=\"noopener noreferrer\">https://www.oskarlindgren.se</a></p>\n",
            "url": "https://okuno.se/blog/creating-a-8-bit-retro-cv-page",
            "title": "Creating a 8 bit Retro CV Page",
            "summary": "I grow up playing SNES (スーパーファミコン) and Sega Mega Drive (メガドライブ). My love for pixel graphic games are absolute. Therefore it made sense for me to build a website in that style to represent me as the geek I was growing up and the person that I still am.",
            "image": "https://okuno.se/blog/oskarlindgren_homepage.png",
            "date_modified": "2018-12-30T00:00:00.000Z"
        }
    ]
}