<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Sergio Marin on Medium]]></title>
        <description><![CDATA[Stories by Sergio Marin on Medium]]></description>
        <link>https://medium.com/@highercomve?source=rss-ea85e17e7579------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*VmiZobhqc48MygWbxL_Taw.jpeg</url>
            <title>Stories by Sergio Marin on Medium</title>
            <link>https://medium.com/@highercomve?source=rss-ea85e17e7579------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 09:28:14 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@highercomve/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[This Is the Dev Setup You’re Looking For: My Journey to Chromebook Development (The Dad Edition)]]></title>
            <link>https://medium.com/@highercomve/this-is-the-dev-setup-youre-looking-for-my-journey-to-chromebook-development-the-dad-edition-eb750f472c7c?source=rss-ea85e17e7579------2</link>
            <guid isPermaLink="false">https://medium.com/p/eb750f472c7c</guid>
            <category><![CDATA[perspective]]></category>
            <category><![CDATA[fatherhood]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Sergio Marin]]></dc:creator>
            <pubDate>Wed, 22 Oct 2025 21:09:25 GMT</pubDate>
            <atom:updated>2025-12-08T17:54:48.110Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kjPfA6LJNO6vx3wz8pKl_Q.jpeg" /></figure><h3>The Preamble: When Life (and a Baby) Confined My Workspace</h3><p>Let’s be real. For as long as I can remember in this game, my setup was all about <strong>raw power</strong>. My rig? A high-spec desktop and a 15&quot; ASUS ROG that sounded like a jet engine when the fans kicked in.</p><p>That ROG was my own personal Death Star — insane firepower, but the battery life? Let’s just say it had a “thermal exhaust port” vulnerability. It was completely useless unless it was plugged into the wall. A “laptop” in name only.</p><p>Then, the kid arrived.</p><p>My “workspace” instantly became “whatever room the baby is in… or sleeping in… or crying in.” My static battle station was useless. I was a dad on the move, and I needed a machine that could move with me.</p><p>The mission was clear: find a machine with a <strong>legendary</strong> battery life that could <strong>still</strong> run my entire, complex stack — Golang web services, React apps, and my specific Pantavisor embedded Linux builds.</p><p>The answer, surprisingly, was a Chromebook.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/579/1*a6vauYJDVPUWebwPdi75fA.png" /></figure><h3>Section 1: The Technical Constraint — Executing Order 66 on Crostini</h3><p>This was the make-or-break moment. To run my embedded builds, I need tools like binfmt and qemu-static. This requires kernel-level access. Standard Crostini (the Linux VM on ChromeOS) is locked down tighter than a diplomatic pouch.</p><p>This <em>was</em> a deal-breaker… until I found the workaround. There’s always a way, right?</p><h4>Giving Your Penguin “Unlimited Power”</h4><p>To transform this sleek, portable machine into a real dev box, you have to give the default Linux container (named ‘penguin’) privileged mode. It’s a quick and dirty trip to the crosh terminal:</p><ol><li><strong>Enable Linux:</strong> First, turn on the Linux environment in your ChromeOS settings.</li><li><strong>Open Crosh:</strong> Hit <strong>Ctrl+Alt+T</strong> to open the crosh terminal.</li><li><strong>Run the Magic Commands:</strong></li></ol><ul><li>Start the VM: vmc start termina</li><li>Stop the container: lxc stop penguin</li><li><strong>The magic:</strong></li></ul><pre>lxc config set penguin security.privileged true<br>lxc config set penguin security.nesting true<br>lxc config set penguin raw.lxc &quot;lxc.cgroup.devices.allow = a&quot;</pre><pre>lxc restart penguin</pre><p>Then you can</p><pre>docker run --privileged --rm tonistiigi/binfmt --install all</pre><pre>docker buildx create \<br>  --name container-builder \<br>  --driver docker-container \<br>  --bootstrap --use</pre><p>This one command grants the kernel access I need. The Chromebook was now a valid, capable environment for my demanding embedded systems development. Game on.</p><h3>Section 2: The Working Toolchain — Linux Without the Headaches</h3><p>Here’s the best part: this is <strong>Linux without the ‘Linux problems.’</strong></p><p>I’ve been a developer long enough to be <em>so tired</em> of fighting with xorg.conf, audio drivers that break after an update, or a webcam that just decides <em>not</em> to work today. With Crostini, the hardware <em>just works</em>. The camera, the (surprisingly amazing) speakers, the mic... all of it. What a relief.</p><p>My entire toolchain ported over with zero drama. VSCode, Zellij, Docker, and all my usual tools from my Arch distro are running happily inside that privileged container.</p><p>But it’s the <em>ChromeOS</em> layer that seals the deal for professional life.</p><ul><li><strong>Communication is Solid:</strong> The 5MP camera is great for meetings, and the quad-speaker setup is so good I don’t even bother with headphones. I can run Android apps like WhatsApp for video calls, and it all feels native.</li><li><strong>The “Second Language” Corrector:</strong> As English is my second language, let’s be honest, sometimes my professional emails can come out sounding… interesting. Having Google’s writing helpers baked into the OS is a lifesaver. It makes my writing better and my communication cleaner. Seriously.</li></ul><h3>Section 3: The Compromise — No Setup is Perfect</h3><p>Alright, it’s not all sunshine and Wookiee hugs. No setup is perfect, and this one has its compromises.</p><p>On my Arch desktop and laptop, I’m used to running the latest, high-performance tools. I actively use GPU-accelerated apps like the zed editor and the ghostty terminal. But... they just don&#39;t work here.</p><p>The Crostini environment, for all its magic, runs on an abstraction layer for its graphics drivers (Vulkan). These new, high-performance apps need direct GPU access that the abstraction just chokes on.</p><p>Is it annoying? Yes. Is it a deal-breaker? Not even close.</p><p>This is where pragmatism wins. My core, battle-tested workhorses are running without a single issue. VSCode, neovim, and zellij (for terminal multiplexing) are all doing their job flawlessly. It’s a small, acceptable trade-off. I&#39;m sacrificing a couple of my favorite new tools from my Arch workflow for the two-day battery life and pure portability that I <em>needed</em> to have.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T6XoyjhMSQeZ8zxhtBlJaw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*56z-9vRtlclLWAw2WuCTWA.png" /></figure><h3>Section 4: The Platform — Kessel Run-Worthy Endurance</h3><p>That old ASUS ROG battery was a Sith Lord, constantly draining the life out of my workflow.</p><p>My new machine is the <strong>Lenovo Chromebook Plus 14</strong>, and its endurance is the Millennium Falcon. It may not look like much, but it’s got it where it counts. The 17-hour battery claim is <strong>not</strong> a marketing lie. I am consistently getting <strong>two full days</strong> of real work — coding, meetings, compiling, and rocking the baby — before I even <em>think</em> about a charger.</p><p>And because it’s running the <strong>MediaTek Kompanio 910 Ultra</strong> chip, it is <strong>fanless</strong>. Silent. I can actually sit and code while the baby is sleeping two feet away. This is the way.</p><p>The performance from this ARM chip is fantastic, even running Go services, Docker, and React builds all at once. The OLED screen is beautiful, and the keyboard and glass trackpad are <em>chef’s kiss</em> perfect.</p><h3>Section 5: The Hybrid Workflow — The Rebel Base Dock</h3><p>Okay, so the I/O is a bit stingy. A couple of USB-C ports and one USB-A. No HDMI.</p><p>But who cares? I already have a “hyperspace docking ring” at my main desk — a big USB-C dock. This gives me the best of both worlds:</p><ol><li><strong>Portable Mode (The Default):</strong> I’m untethered, working from the couch, the kitchen, or the nursery, living on that two-day battery.</li><li><strong>Docked Mode (The “Rebel Base”):</strong> When I need to go heads-down for a massive compile, I just plug in that <em>one</em> USB-C cable. Instantly, I’m connected to my giant monitor, my mechanical keyboard, and all my peripherals.</li></ol><h3>Section 6: The Obvious Question — Why Not a MacBook Air?</h3><p>This is the elephant in the room, right? Especially when you look at the price.</p><p>An M4 MacBook Air is an incredible machine. You can find one on Amazon for around <strong>$850</strong> on a good day. The list price for my <strong>Lenovo Chromebook Plus 14</strong> is <strong>$750</strong>. For only $100 more, the M4 <em>smokes</em> the Kompanio chip in raw benchmarks. It’s not even a fair fight, as you can see from the Geekbench scores.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VkKkmDwfPM0ESjdljOpguA.png" /></figure><p>But here’s the kicker: I found my Chromebook on sale for <strong>$600</strong>.</p><p>Now we’re talking a <strong>$250 price difference</strong>. That’s not just “a little cheaper,” that’s a whole different budget category.</p><p>But honestly, even if the prices had been identical, I still would have gone with the Chromebook. It came down to two simple things: <strong>the workflow and the ecosystem.</strong></p><p>First, the workflow. I’m not just a web dev. A huge part of my job is building embedded Linux systems. macOS is <em>Unix-like</em>, but it is <strong>not</strong> <em>Linux</em>. On the Chromebook, I get a true, privileged Debian VM. When I’m working with qemu and binfmt for my cross-architecture builds, I am in a <em>native</em> Linux environment that&#39;s nearly identical to my production targets. Docker on Mac is great, but it&#39;s still an abstraction. I prefer being closer to the metal.</p><p>Second, and just as important, is the ecosystem. I’m a Google guy. I use a Pixel phone. The tight integration with the OS, the “second language” corrections, and the built-in Gemini features are all valuable to me.</p><p>I don’t own a single other Apple product. No iPhone, no iPad. The famous “Apple ecosystem” is a premium I’d be paying for but would <em>never</em> use. I’m just not an Apple fan.</p><p>So yeah, the M4 is faster. But for my specific, native-Linux workflow, my Google-first life, and (thanks to a great sale) a much better price, the Chromebook was the <em>right</em> tool, not just the “cheaper” one.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vntJ63DKP6rRB1nc8_mAQw.png" /></figure><h3>Conclusion: A Refined Weapon for a More Civilized Age</h3><p>This move wasn’t just an “upgrade.” It was a complete shift in how I work.</p><p>This Chromebook lets me be a professional dev — running Go, React, and privileged embedded builds — and <em>also</em> a 100% engaged new dad. It’s the setup I didn’t know I needed.</p><p>It’s hands-down my favorite machine ever, and it’s the one I now recommend to any professional dev who wants power <em>and</em> freedom.</p><p>May the Force (and the battery) be with you.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=eb750f472c7c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Rendering React on Golang with V8Go]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://itnext.io/rendering-react-on-golang-with-v8go-fd84cdad2844?source=rss-ea85e17e7579------2"><img src="https://cdn-images-1.medium.com/max/1792/0*5jSlqjw2CkVBCHcm" width="1792"></a></p><p class="medium-feed-snippet">Server-side rendering (SSR) is essential for improving web performance, SEO, and initial page load experience. React provides built-in SSR&#x2026;</p><p class="medium-feed-link"><a href="https://itnext.io/rendering-react-on-golang-with-v8go-fd84cdad2844?source=rss-ea85e17e7579------2">Continue reading on ITNEXT »</a></p></div>]]></description>
            <link>https://itnext.io/rendering-react-on-golang-with-v8go-fd84cdad2844?source=rss-ea85e17e7579------2</link>
            <guid isPermaLink="false">https://medium.com/p/fd84cdad2844</guid>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[reactjs]]></category>
            <category><![CDATA[server-side-rendering]]></category>
            <dc:creator><![CDATA[Sergio Marin]]></dc:creator>
            <pubDate>Sun, 09 Feb 2025 09:04:59 GMT</pubDate>
            <atom:updated>2025-02-09T09:04:59.529Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Parsing TAR files with Plain JavaScript]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://itnext.io/parsing-tar-files-with-plain-javascript-9541c3f23982?source=rss-ea85e17e7579------2"><img src="https://cdn-images-1.medium.com/max/1024/1*zyc90LnRBFy6-3-7ODu0Gg.png" width="1024"></a></p><p class="medium-feed-snippet">In the world of web development, there&#x2019;s often a need to handle various file formats directly in the browser. One such format is TAR (Tape&#x2026;</p><p class="medium-feed-link"><a href="https://itnext.io/parsing-tar-files-with-plain-javascript-9541c3f23982?source=rss-ea85e17e7579------2">Continue reading on ITNEXT »</a></p></div>]]></description>
            <link>https://itnext.io/parsing-tar-files-with-plain-javascript-9541c3f23982?source=rss-ea85e17e7579------2</link>
            <guid isPermaLink="false">https://medium.com/p/9541c3f23982</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[compression]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Sergio Marin]]></dc:creator>
            <pubDate>Fri, 27 Sep 2024 17:26:23 GMT</pubDate>
            <atom:updated>2024-09-27T20:52:17.426Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Why did I remove every Javascript build step on my website]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://itnext.io/why-did-i-remove-every-build-step-on-my-website-181e96a45a9c?source=rss-ea85e17e7579------2"><img src="https://cdn-images-1.medium.com/max/1380/1*RDuXIqGiKPkeK8InDw-PYA.png" width="1380"></a></p><p class="medium-feed-snippet">Introduction</p><p class="medium-feed-link"><a href="https://itnext.io/why-did-i-remove-every-build-step-on-my-website-181e96a45a9c?source=rss-ea85e17e7579------2">Continue reading on ITNEXT »</a></p></div>]]></description>
            <link>https://itnext.io/why-did-i-remove-every-build-step-on-my-website-181e96a45a9c?source=rss-ea85e17e7579------2</link>
            <guid isPermaLink="false">https://medium.com/p/181e96a45a9c</guid>
            <category><![CDATA[html]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Sergio Marin]]></dc:creator>
            <pubDate>Mon, 16 Sep 2024 22:06:51 GMT</pubDate>
            <atom:updated>2025-02-07T17:49:46.208Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Detecting Memory Leaks in Zig Using the General Purpose Allocator]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://itnext.io/detecting-memory-leaks-in-zig-using-the-general-purpose-allocator-b63be2cbd1f5?source=rss-ea85e17e7579------2"><img src="https://cdn-images-1.medium.com/max/1024/1*IJmvOdxGUZOaaW9ztmN_hw.png" width="1024"></a></p><p class="medium-feed-snippet">In the past few months, I have been learning Zig, and in that time, I created a couple of things with Zig. One was a web server with a&#x2026;</p><p class="medium-feed-link"><a href="https://itnext.io/detecting-memory-leaks-in-zig-using-the-general-purpose-allocator-b63be2cbd1f5?source=rss-ea85e17e7579------2">Continue reading on ITNEXT »</a></p></div>]]></description>
            <link>https://itnext.io/detecting-memory-leaks-in-zig-using-the-general-purpose-allocator-b63be2cbd1f5?source=rss-ea85e17e7579------2</link>
            <guid isPermaLink="false">https://medium.com/p/b63be2cbd1f5</guid>
            <category><![CDATA[zig]]></category>
            <category><![CDATA[memory-leak]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[memory-management]]></category>
            <dc:creator><![CDATA[Sergio Marin]]></dc:creator>
            <pubDate>Mon, 09 Sep 2024 15:14:52 GMT</pubDate>
            <atom:updated>2024-09-09T16:13:11.266Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Golang and HTMX combination for embedded Linux]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://itnext.io/golang-and-htmx-combination-for-embedded-linux-8169887104e5?source=rss-ea85e17e7579------2"><img src="https://cdn-images-1.medium.com/max/2048/1*QHADAXGFNtIYLpU6il4BVw.png" width="2048"></a></p><p class="medium-feed-snippet">For the past six years, I&#x2019;ve been working at Pantacor, a company that develops an open-source framework for containerized embedded Linux&#x2026;</p><p class="medium-feed-link"><a href="https://itnext.io/golang-and-htmx-combination-for-embedded-linux-8169887104e5?source=rss-ea85e17e7579------2">Continue reading on ITNEXT »</a></p></div>]]></description>
            <link>https://itnext.io/golang-and-htmx-combination-for-embedded-linux-8169887104e5?source=rss-ea85e17e7579------2</link>
            <guid isPermaLink="false">https://medium.com/p/8169887104e5</guid>
            <category><![CDATA[embedded-systems]]></category>
            <category><![CDATA[html]]></category>
            <category><![CDATA[golang]]></category>
            <category><![CDATA[htmx]]></category>
            <dc:creator><![CDATA[Sergio Marin]]></dc:creator>
            <pubDate>Fri, 06 Sep 2024 20:56:57 GMT</pubDate>
            <atom:updated>2024-09-07T14:24:40.798Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Optimize your docker containers]]></title>
            <link>https://itnext.io/optimize-your-docker-containers-6255545dfa54?source=rss-ea85e17e7579------2</link>
            <guid isPermaLink="false">https://medium.com/p/6255545dfa54</guid>
            <category><![CDATA[dockerfiles]]></category>
            <category><![CDATA[docker-image]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Sergio Marin]]></dc:creator>
            <pubDate>Sat, 22 Oct 2022 22:16:01 GMT</pubDate>
            <atom:updated>2025-02-11T23:28:12.571Z</atom:updated>
            <content:encoded><![CDATA[<p>You might have been building containers for a long time, and in all your previous builds, the security and/or the size of your containers wasn’t the priority. But if you got here now you are giving priority to those topics.</p><p>For now we can focus first on the container size, and for this let&#39;s talk about a couple of options here.</p><p>- Build upon a minimal base image.<br>- Use multi-stage builds.<br>- Create containers with statically linked applications.</p><h3>Build upon a minimal base image</h3><p>Instead of using a big image like ubuntu, debian, etc., which could be a huge image for what is needed, we can use some alternatives that will allow us to start from a much smaller base.</p><pre>debian latest d91720f514f7 124MB<br>ubuntu latest 216c552ea5ba 77.8MB</pre><pre>alpine latest 9c6f07244728 5.54MB<br>gcr.io/distroless/static-debian11 latest 561bdfb51245 2.35MB</pre><p>The alpine and google distroless container are way smaller than ubuntu or debian.</p><p>In the case of google distroless container, the only problem will be the package management for that container, because it won&#39;t have any package manager, another thing “missing” will be a shell. But they have a good number of base images for several runtime.</p><pre>gcr.io/distroless/static-debian11<br>gcr.io/distroless/base-debian11<br>gcr.io/distroless/cc-debian11<br>gcr.io/distroless/python3-debian11<br>gcr.io/distroless/java-base-debian11<br>gcr.io/distroless/java11-debian11<br>gcr.io/distroless/java17-debian11<br>gcr.io/distroless/nodejs-debian11</pre><p>Alpine, on the other hand, is a distro container. Therefore, it has a package manager and a shell, but it is small enough to be used as a base container.</p><p>Let&#39;s start with an easy-go application, you can see the source code of the application <a href="https://github.com/highercomve/better-container-example">here</a>. The application will expose a web application in port 5000 with general information about the system by default.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*qj4qPO7l7DVa22cekRCcHg.png" /></figure><p>If we start with the basic way of building this container, will be something like this.</p><pre>FROM golang<br>WORKDIR /app<br>COPY . /app/<br>RUN go mod download; \<br>    CGO_ENABLED=0 go build -ldflags=”-s -w” -o bce -v .<br>EXPOSE 5000<br>ENTRYPOINT [“/app/bce”]</pre><p>And after this, we can build the container with the command:</p><pre>docker build -t highercomve/bce .</pre><p>After this, we will end with a whooping 1.02GB size for the container.</p><pre>highercomve/bce latest 614d597dbfb5 4 seconds ago 1.02GB</pre><p>That doesn’t sound that good. Let&#39;s now change the base container from <strong>golang</strong> to <strong>golang:alpine</strong>.</p><pre>FROM golang:alpine<br>WORKDIR /app<br>COPY . /app/<br>RUN go mod download; \<br>    CGO_ENABLED=0 go build -ldflags=”-s -w” -o bce -v .<br>EXPOSE 5000<br>ENTRYPOINT [“/app/bce”]</pre><p>And with only this change, we now are in only 375MB</p><pre>highercomve/bce latest 1fb5114c906e About a minute ago 375MB</pre><p>It has the same container, the same application, and a lot less space.</p><h3>Use multi-stage builds</h3><p>Now the second optimization we can do is use a multi-stage build. For this, we need to mark in the FROM command the name the stage will have, and then we use another base as the final runtime of the container.</p><pre>FROM golang:alpine as builder # FIRST STAGE<br>WORKDIR /app<br>COPY . /app/<br>RUN go mod download; \<br>    CGO_ENABLED=0 go build -ldflags=”-s -w” -o bce -v .</pre><pre>FROM gcr.io/distroless/static-debian11<br>WORKDIR /app<br>COPY — from=builder /app/bce /app/bce # IMPORT FROM THAT STAGE<br>COPY static /app/static/<br>COPY templates /app/templates/<br>EXPOSE 5000<br>ENTRYPOINT [“/app/bce”]</pre><p>Now after this change, we can see the difference it will be huge, because the resulting image will only be 8.73MB in size.</p><pre>highercomve/bce latest e68c3ebb73e8 About a minute ago 8.73MB</pre><h3>Create containers with statically linked applications</h3><p>Another thing you could do is build the application with the libraries linked and use the scratch base container.</p><pre>FROM golang:alpine as builder<br>WORKDIR /app<br>COPY . /app/<br>RUN go mod download; \<br> CGO_ENABLED=0 go build -ldflags=”-s -w -extldflags=-static” -o bce -v .</pre><pre>FROM scratch<br>WORKDIR /app<br>COPY — from=builder /app/bce /app/bce<br>COPY static /app/static/<br>COPY templates /app/templates/<br>EXPOSE 5000<br>ENTRYPOINT [“/app/bce”]</pre><p>With the change we will reduce another 2MB in size</p><pre>highercomve/bce latest f32c277bf5a6 7 seconds ago 6.39MB</pre><p>Now as a bonus, we will add how to use this technique to build a multi arch build container. Maybe you have the same application, but you need to run it in arm or riscv architecture. For this we can use the buildx plugin from docker <a href="https://github.com/docker/buildx](https://github.com/docker/buildx).">https://github.com/docker/buildx</a>.</p><p>And for the same container, I will use two arguments that are going to be injected by buildx.</p><pre>ARG TARGETPLATFORM<br>ARG BUILDPLATFORM</pre><p>And will allow us to make decisions about how to configure the environment variables for Golang. I will use this build.sh script to map the target platform variable injected by buildx to the Golang environment variables for building.</p><pre>#!/bin/sh<br>set -e</pre><pre>echo &quot;Building for $TARGETPLATFORM&quot; <br>export CGO_ENABLED=0</pre><pre>case &quot;$TARGETPLATFORM&quot; in<br> &quot;linux/arm/v6&quot;*)<br>  export GOOS=linux GOARCH=arm GOARM=6<br>  ;;<br> &quot;linux/arm/v7&quot;*)<br>  export GOOS=linux GOARCH=arm GOARM=7<br>  ;;<br> &quot;linux/arm64&quot;*)<br>  export GOOS=linux GOARCH=arm64 GOARM=7<br>  ;;<br> &quot;linux/386&quot;*)<br>  export GOOS=linux GOARCH=386<br>  ;;<br> &quot;linux/amd64&quot;*)<br>  export GOOS=linux GOARCH=amd64<br>  ;;<br> &quot;linux/mips&quot;*)<br>  export GOOS=linux GOARCH=mips<br>  ;;<br> &quot;linux/mipsle&quot;*)<br>  export GOOS=linux GOARCH=mipsle<br>  ;;<br> &quot;linux/mips64&quot;*)<br>  export GOOS=linux GOARCH=mips64<br>  ;;<br> &quot;linux/mips64le&quot;*)<br>  export GOOS=linux GOARCH=mips64le<br>  ;;<br> &quot;linux/riscv64&quot;*)<br>  export GOOS=linux GOARCH=riscv64<br>  ;;<br> *)<br>  echo &quot;Unknown machine type: $machine&quot;<br>  echo &quot;Building using host architecture&quot;<br>esac</pre><pre>go mod download<br>go build -ldflags=&quot;-s -w -extldflags=-static&quot; -o bce -v .</pre><p>We need to be sure of one thing, the compilation process needs to run in the same architecture as the host building the container, if not, we may lose performance through emulation. For this we can use the <strong>BUILDPLATFORM</strong> argument in the <strong>FROM</strong> definition on the dockerfile.</p><p>Something like this.</p><pre>FROM — platform=$BUILDPLATFORM golang:alpine as builder<br>ARG TARGETPLATFORM<br>ARG BUILDPLATFORM<br>WORKDIR /app<br>COPY . /app/<br>RUN /app/build.sh</pre><pre>FROM scratch<br>WORKDIR /app<br>COPY — from=builder /app/bce /app/bce<br>COPY static /app/static/<br>COPY templates /app/templates/<br>EXPOSE 5000<br>ENTRYPOINT [“/app/bce”]</pre><p>With this new Dockerfile we can build the container using the buildx plugin.</p><pre>docker buildx build — platform linux/arm64 -t highercomve/bce — load .</pre><p>Here we introduce 3 new arguments for the build command:</p><p>- platform: here we can define a list separated by coma of several architectures<br>- load: this argument configures docker buildx to load the image to our local docker environment after the build process is done.<br>- push: this will push to docker hub all the container images after the building process is done.</p><p>You can build several platforms in one command and push them to the docker hub.</p><pre>docker buildx build — platform linux/arm64,linux/amd64,linux/arm — push -t highercomve/bce .</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*qCs8HhPHH1tb0p635A78TA.png" /></figure><p>Docker hub will support one image with multiple architectures and when some device pulls with one of those architectures will get the correct one without any problem.</p><p>If we want to test the container running with one architecture different from one of our systems we will need to load the qemu binaries.</p><pre>docker run — rm — privileged multiarch/qemu-user-static — reset -p yes</pre><p>And after that run the container with the platform we like</p><pre>docker run -it -p 5000:5000 — platform linux/arm64 highercomve/bce</pre><p>In this example, the output of the web page should be something like this</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*XJ6iWw0hrGMlNJuASVD5_w.png" /></figure><p>I hope this will be useful for you and help you to maintain more optimized containers</p><p><strong>May the force be with you</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6255545dfa54" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/optimize-your-docker-containers-6255545dfa54">Optimize your docker containers</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Running Containerized GUIs on LXC with Pantavisor Linux]]></title>
            <link>https://medium.com/@highercomve/running-containerized-guis-on-lxc-with-pantavisor-linux-4e872772f5a?source=rss-ea85e17e7579------2</link>
            <guid isPermaLink="false">https://medium.com/p/4e872772f5a</guid>
            <category><![CDATA[lxc]]></category>
            <category><![CDATA[containers]]></category>
            <category><![CDATA[gui]]></category>
            <dc:creator><![CDATA[Sergio Marin]]></dc:creator>
            <pubDate>Sun, 12 Sep 2021 23:26:43 GMT</pubDate>
            <atom:updated>2021-09-12T23:26:43.218Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*eD2CGJYG80mYacl3.jpg" /></figure><p>Pantavisor Linux is an embedded framework for building, maintaining, and managing LXC containers. It presents an infinite number of possibilities for running applications at the smart device edge.</p><p>In this use case, we’ll describe how to run containerized GUI applications that communicate via a socket to a containerized version of <a href="https://wayland.freedesktop.org/">Wayland Composer</a>.</p><h3>Use Case</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*2eWgKJNhnQBQRwK2.png" /></figure><p>We will run two different applications for this example:</p><ul><li><strong>qt</strong>: An opensource application written in <a href="https://www.qt.io/">QT</a>. You can see the source code for the application <a href="https://github.com/onyazuka/Simple-system-monitor">here</a>. The built container for it is here: <a href="https://hub.docker.com/r/highercomve/qt-system-monitor">https://hub.docker.com/r/highercomve/qt-system-monitor</a></li><li><strong>flutter-example</strong>: This is another application built with <a href="https://flutter.dev/">Flutter</a>. Flutter is a framework developed by Google to build iOS, Android, and Desktop apps written in <a href="https://dart.dev/multiplatform-apps">Dart</a>. We will use the default <a href="https://flutter.dev/docs/get-started/test-drive?tab=vscode#create-app">example Flutter app from Flutter</a></li></ul><p>Both <strong>qt</strong> and <strong>flutter-example</strong> are built with Ubuntu containers that run in another Ubuntu container.</p><p>You can see the source code for the application containers here:</p><ul><li><strong>Flutter-example</strong>: <a href="https://gitlab.com/highercomve/flutter-weston-example">https://gitlab.com/highercomve/flutter-weston-example</a></li><li><strong>qt</strong>: <a href="https://gitlab.com/highercomve/qt-simple-system-monitor">https://gitlab.com/highercomve/qt-simple-system-monitor</a></li></ul><p>For the Wayland protocol and compositor, we selected <a href="https://gitlab.freedesktop.org/wayland/weston">weston</a>. Weston can be described as the reference implementation of a Wayland compositor. It is also a very useful environment in and of itself.</p><p>With this, we now have the three base containers that will do the work of running the application and then displaying it on the monitor.</p><h3>Before You Begin</h3><p>You need a Raspberry PI 4, a display, an HDMI cable, and a mouse and keyboard connected to the Raspberry PI. By default, weston does not start without a connected keyboard and mouse.</p><p>You can download a Pantavisor image and claim it at <a href="https://hub.pantacor.com">hub.pantacor.com.</a> If you don’t have an account on <a href="http://hub.pantacor.com">hub.pantacor.com</a>, create one and then claim the device by following this <a href="https://docs.pantahub.com/get-started/">Getting started guide</a>.</p><h3>How this works</h3><p>Because this demo is fairly straightforward, and also to make things simpler, you can clone and deploy my reference device directly to your device with:</p><pre>pvr clone https://pvr.pantahub.com/highercomve/gui_rpi64<br>cd gui_rpi64<br>pvr post -m &quot;Add weston, flutter example and qt example&quot; YOUR_DEVICE_CLONE_URL</pre><p>Those commands will push all the parts needed to your device so that you can see everything running. Inside the reference device is the following folder structure:</p><ul><li>bsp</li><li>awconnect</li><li>pv-avahi</li><li>weston</li><li>flutter-example</li><li>qt</li></ul><p>With all those containers we are going to build a communication system for the Wayland components that will work in this way:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*wazsBxRSrwnZm4pn.png" /></figure><p>You can read more about how Wayland works in their documentation about the architecture. But let’s dig into how this container works from the pantavisor perspective and how every container is built and how it works.</p><h3>BSP</h3><p>The bsp folder contains the Board Support Package. It is also where Pantavisor runs.</p><h3>awconnect</h3><p>This is the main container and platform for the system. It is very similar to the base OS for all running containers. The awconnect container is also the central point in the example app architecture with weston. Weston will use any sockets configured inside the awconnect container. This container will then pass the info down to the kernel, etc.</p><h3>Sharing sockets in the awconnect container</h3><p>To share sockets running in the main container, you must configure the storage parameters inside the awconnect src.json file.</p><p>First, mount a permanent volume inside the /var/run/dbus folder of awconnect. This allows you to read and write from any container that wants to send its data to the monitor.</p><p>To do that the awconnect src.json should look as follows:</p><pre>{<br>  &quot;#spec&quot;: &quot;service-manifest-src@1&quot;,<br>  &quot;args&quot;: {},<br>  &quot;config&quot;: {},<br>  &quot;docker_digest&quot;: &quot;registry.gitlab.com/pantacor/pv-platforms/wifi-connect@sha256:5c889720c6243408049b7a2f9ec75b9f4e376f6cbc28e39e091420a1e19df2aa&quot;,<br>  &quot;docker_name&quot;: &quot;registry.gitlab.com/pantacor/pv-platforms/wifi-connect&quot;,<br>  &quot;docker_source&quot;: &quot;remote,local&quot;,<br>  &quot;docker_tag&quot;: &quot;arm32v5&quot;,<br>  &quot;persistence&quot;: {<br>    &quot;/var/run/dbus&quot;: &quot;boot&quot;<br>  },<br>  &quot;template&quot;: &quot;builtin-lxc-docker&quot;<br>}</pre><h3>How the Weston container is built</h3><p>The weston container is built with alpine and also includes all of the packages to make the Wayland Compositor work with weston.</p><p>You can see the source of this container <a href="https://gitlab.com/pantacor/pv-platforms/alpine-weston">here</a>.</p><p>The Dockerfile generates the container. It will look exactly like any other normal Dockerfile, since there is nothing different that needs to be done to make a container run in Pantavisor.</p><p>However, the most important part is not the container itself, but rather the Pantavisor configuration inside the src.json file. It is here that we need to be able to read and write to the socket folder kept inside the awconnect container.</p><pre>{<br>  &quot;#spec&quot;: &quot;service-manifest-src@1&quot;,<br>  &quot;args&quot;: {<br>    &quot;PV_LXC_EXTRA_CONF&quot;: &quot;lxc.mount.entry = /volumes/awconnect/docker--var-run-dbus run/dbus none bind,rw,create=dir 0 0\n&quot;,<br>    &quot;PV_RUNLEVEL&quot;: &quot;platform&quot;<br>  },<br>  &quot;config&quot;: {},<br>  &quot;docker_digest&quot;: &quot;sha256:116cc7d42d3f7e513fad55688b618e61dcdae2dd37f8164a86eea80af00c03d9&quot;,<br>  &quot;docker_name&quot;: &quot;weston&quot;,<br>  &quot;docker_source&quot;: &quot;remote,local&quot;,<br>  &quot;docker_tag&quot;: &quot;latest&quot;,<br>  &quot;persistence&quot;: {},<br>  &quot;template&quot;: &quot;builtin-lxc-docker&quot;<br>}</pre><p>All the docker_* parts of this file are generated using:</p><pre>pvr app add --from=registry.gitlab.com/pantacor/pv-platforms/alpine-weston:arm64v8 weston</pre><h3>Adding Docker apps to Pantavisor</h3><p>You can use the pvr app add command to add more Docker applications, and to basically convert any Docker container into a Pantavisor container (just a normal LXC container but with our tooling to make it easy to manage). See: <a href="https://docs.pantahub.com/pvr-docker-apps-experience/">Adding Apps from Docker</a> for more information.</p><h3>Communicating with the awconnect</h3><p>To enable the weston container to talk to the awconnect system, the following two arguments were added:</p><p><strong>1.- PV_LXC_EXTRA_CONF</strong></p><p>This configuration allows the pvr commands to extend the default LXC generated configuration. It is necessary to add extra LXC configuration in this way because when pvr updates or installs the container it also needs to generate the LXC configuration file.</p><p>In here we configure the lXC container to mount the same volume of the /var/run/dbus on awconnect inside a /run/dbus folder with read/write permissions.</p><p>The route of the awconnect volume is defined in the run.json file for that container. We will mount it using the LXC configuration parameter lxc.mount.entry</p><p>You can read more about the LXC configuration in the <a href="https://linuxcontainers.org/lxc/manpages/man5/lxc.container.conf.5.html">documentation</a></p><p>The end result of that configuration will be:</p><pre>lxc.mount.entry = /volumes/awconnect/docker--var-run-dbus run/dbus none bind,rw,create=dir 0 0</pre><p><strong>2.- PV_RUNLEVEL</strong></p><p>The PV_RUNLEVEL argument configures the level of priority used to run the container. By default, all applications run in RUNLEVEL: app and all apps in that run level will wait for the platforms to start.</p><p>With this configuration, the container should run correctly and it will act as the wayland composer.</p><h3>flutter-example and qt</h3><p>And lastly, we have the containers that will run the GUI applications. As I mentioned before, they are built with this source code:</p><ul><li><strong>flutter-example</strong>: <a href="https://gitlab.com/highercomve/flutter-weston-example">https://gitlab.com/highercomve/flutter-weston-example</a></li><li><strong>qt</strong>: <a href="https://gitlab.com/highercomve/qt-simple-system-monitor">https://gitlab.com/highercomve/qt-simple-system-monitor</a></li></ul><p>And the configuration for both of them will be similar to the weston platform where we are going to modify the args value of the src.json in order to mount the awconnect volume with the sockets and configure both containers to run on RUNLEVEL platform.</p><h3>Display the results</h3><p>After you’ve posted everything to your device and the device consumed the revision, the Raspberry Pi will restart. You will then see the System monitor application written on QT and the Flutter example of a counter clicker running on the Raspberry PI display.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*eD2CGJYG80mYacl3.jpg" /></figure><p>Is important to know that you can install to a container any available application from your favorite Linux distribution (example: Ubuntu) with support for wayland. That means you will install more packages than it needs, but it is possible.</p><p>I recommend that you look insides of the flutter-example and the QT system monitor containers to see the dependencies needed for the applications to run.</p><h3>Final Thoughts</h3><p>If you have any questions or you want to know more about <a href="https://www.pantavisor.io">Pantavisor</a> and <a href="https://hub.pantacor.com">hub.pantacor.com</a> you can reach out to us on <a href="https://twitter.com/pantahub">@pantahub</a> on Twitter or join our <a href="https://pantavisor.slack.com">Pantavisor Slack channel</a>. We’d be happy to help.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4e872772f5a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to do Port Forwarding on LXC with Awall]]></title>
            <link>https://itnext.io/how-to-do-port-forwarding-on-lxc-with-awall-bd498da69164?source=rss-ea85e17e7579------2</link>
            <guid isPermaLink="false">https://medium.com/p/bd498da69164</guid>
            <category><![CDATA[raspberry-pi]]></category>
            <category><![CDATA[port-forwarding]]></category>
            <category><![CDATA[lxc]]></category>
            <dc:creator><![CDATA[Sergio Marin]]></dc:creator>
            <pubDate>Mon, 23 Aug 2021 22:23:17 GMT</pubDate>
            <atom:updated>2021-08-24T15:31:22.364Z</atom:updated>
            <content:encoded><![CDATA[<h3>Intro</h3><p>We are going to be running our LXC containers with <a href="https://www.pantavisor.io">pantavisor.io </a>a Linux system to run containers on embedded devices built by <a href="https://www.pantacor.com">Pantacor</a>, in this way we will reduce the hassle of managing LXC containers, and we are going to run everything with a Raspberry PI 4.</p><p>Pantavisor uses LXC to run containers on embedded computers. By default, all containers share the host’s network namespace and as a result, the LXC configuration for the network will be:</p><p><strong>lxc.net.[0].type = none</strong></p><p><a href="https://linuxcontainers.org/lxc/manpages/man5/lxc.container.conf.5.html#lbAO">LXC — Manpages — lxc.container.conf.5</a></p><p>This means that if you have more than one container using the same network <strong>PORT,</strong> the container initialization will fail. A way to solve that problem is to create the containers with a virtual ethernet type of network and then do a port forwarding from the host’s network to the container IP.</p><p>In Docker, port forwarding is a straightforward configuration when you run the container. But for LXC containers it is not that straightforward. In this post, we will describe how to forward ports inside the <em>pantavisor. </em>With Pantavisor tools you will create the LXC configuration and then use <em>awall</em> to build the container IP tables that will manage the forwarding.</p><h3>Configuring Raspberry Pi as a DNS server for Adguard</h3><p>In this use case, you will use a Raspberry PI as a DNS server with the <a href="https://hub.docker.com/r/adguard/adguardhome">adguard</a> app that will filter any ads directly from DNS.</p><p>First, let’s observe the Adguard ports on Docker with:</p><pre>docker run --name adguardhome \<br>    --restart unless-stopped \<br>    -v /my/own/workdir:/opt/adguardhome/work \<br>    -v /my/own/confdir:/opt/adguardhome/conf \<br>    -p 53:53/tcp -p 53:53/udp \<br>    -p 67:67/udp -p 68:68/udp \<br>    -p 3000:3000/tcp \<br>    -p 853:853/tcp \<br>    -p 784:784/udp -p 853:853/udp -p 8853:8853/udp \<br>    -p 5443:5443/tcp -p 5443:5443/udp \<br>    -d adguard/adguardhome</pre><p>The ports to map are:</p><ul><li><strong>53 tcp/udp</strong>: Plain DNS.</li><li><strong>67/udp, 68 tcp/udp</strong>: Add if you intend to use AdGuard Home as a DHCP server.</li><li><strong>3000/tcp</strong>: Add if you want to use AdGuard Home’s admin panel and also run AdGuard Home as an HTTPS/DNS-over-HTTPS server.</li><li><strong>853/tcp</strong>: Add if you plan to run AdGuard Home as a DNS-over-TLS server.</li><li><strong>784/udp , 853 udp, 8853/udp</strong>: Add if you are going to run AdGuard Home as a DNS-over-QUIC server. You may only leave one or two of these.</li><li><strong>5443/tcp, 5443/udp:</strong> Add if you are going to run AdGuard Home as a DNSCrypt server.</li></ul><p>In the Pantavisor schema, the <a href="https://gitlab.com/pantacor/pv-platforms/wifi-connect">awconnect</a> container does all of the network management for the default image for Pantavisor on Raspberry PI. This is what allows you to connect the Raspberry Pi to the internet via wifi or to share your wired internet through a wifi hotspot.</p><p>It is this container that provides DNS and DHCP. In addition to this, you may have another container using port 3000.</p><h3>Before You Begin</h3><p>In order to work on this, we need to have a Pantavisor-enabled Raspberry PI 3+ or a Raspberry PI 4. You will also need to install the following on your development computer:</p><ol><li>fakeroot</li><li>squashfs</li><li>Docker</li><li>pvr cli</li><li>Sign up for an account on <a href="https://hub.pantacor.com/">hub.pantacor.com</a></li></ol><p>You can install <strong>fakeroot</strong> and <strong>squashfs</strong> from your distro or with your OS package manager: apt, yum, apk, homebrew, etc.</p><p>For example Ubuntu/Debian based distros install these utilities with:</p><pre>sudo apt install fakeroot squashfs-tools</pre><p>To install Docker for your particular OS, refer to the Docker <a href="https://docs.docker.com/engine/install/">installation documentation</a></p><p>Last but not least, you will also need to install the PVR cli to manage <a href="https://www.pantavisor.io/">pantavisor</a> devices inside <a href="https://hub.pantacor.com/">hub.pantacor.com</a>. You can find more about how to install PVR in Pantacor <a href="https://docs.pantahub.com/install-pvr/">documentation</a>.</p><p>Because we will send new revisions and configurations to the device remotely from your development computer, the device needs to connect to the <a href="https://hub.pantacor.com/">hub.pantacor.com</a> cloud service. You will need to create an account there.</p><p>Now that you have everything installed, we can move forward to solve our network isolation and port forwarding problem.</p><h3>#1 Installing Pantavisor and claiming the device</h3><p>A claiming process may enable an end-user to claim a device and proof ownership thereof. Such a process is initially triggered via a claim message that could be done in several ways.</p><p>After you’ve installed the Pantavisor image or if you installed Pantavisor already, you need to claim your device. There are several ways to claim your device:</p><p><strong>1.- Using pre-claimed images</strong></p><p>The easiest way to claim the device is by downloading an image that automatically claims it on the first boot. Download this image from: <a href="https://hub.pantacor.com/download-image">https://hub.pantacor.com/download-image</a></p><p>2.- <strong>Manual claiming</strong></p><p>Another way to claim is by following the <a href="https://www.pantavisor.io/guides/getting_started/">getting started guide</a> on pantavisor.io and then claiming the device manually with the PVR CLI from your development computer:</p><pre>pvr device scan</pre><p>The result of that command should look something like this:</p><pre>sergiomarin@penguin:~$ pvr device scan<br>Scanning ...<br>        ID: 61147e8af0e0f5000a50d11c (unclaimed)<br>        Host: localhost.local.<br>        IPv4: [192.168.68.115]<br>        IPv6: [fe80::d4ac:9722:ab19:7b81]<br>        Port: 22<br>        Claim Cmd: pvr claim -c marginally-optimum-midge https://api.pantahub.com:443/devices/61147e8af0e0f5000a50d11c<br>Pantavisor devices detected in network: 1 (see above for details)</pre><p>And after that take the Claim Cmd value and run that. For example:</p><pre>pvr claim -c marginally-optimum-midge <a href="https://api.pantahub.com:443/devices/61147e8af0e0f5000a50d11c">https://api.pantahub.com:443/devices/61147e8af0e0f5000a50d11c</a></pre><p>After this step, you will be able to see your claimed device at <a href="https://hub.pantacor.com/">hub.pantacor.com</a> on the devices page.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*HT4rvCHSBXNdEOwZ" /></figure><p>You will see a new device with the status <strong>SYNCING. </strong>, This means your device has been claimed and is uploading to the cloud. When that process is <strong>DONE, </strong>you will be able to clone and modify the device.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*VRFaxcaZkRL9HJpf" /></figure><h3>#2 Cloning the device URL to your computer</h3><p>In the device details panel, you will see the <strong>Clone URL</strong>. Copy the URL and run the following command on your development laptop:</p><pre>pvr clone CLONE_URL</pre><p>Example:</p><pre>pvr clone <a href="https://pvr.pantahub.com/highercomve/specially_brief_monkey">https://pvr.pantahub.com/highercomve/specially_brief_monkey</a></pre><p>That creates a new folder called `<strong>specially_brief_monkey`</strong> much like the way `git clone` works. Inside the folder you should see a structure similar to this:</p><pre>specially_brief_monkey<br>├── awconnect<br>│   ├── lxc.container.conf<br>│   ├── root.squashfs<br>│   ├── root.squashfs.docker-digest<br>│   ├── run.json<br>│   └── src.json<br>├── bsp<br>│   ├── addon-plymouth.cpio.xz4<br>│   ├── build.json<br>│   ├── firmware.squashfs<br>│   ├── kernel.img<br>│   ├── modules.squashfs<br>│   ├── pantavisor<br>│   ├── run.json<br>│   └── src.json<br>├── _hostconfig<br>│   └── pvr<br>│       └── docker.json<br>├── network-mapping.json<br>├── pv-avahi<br>│   ├── lxc.container.conf<br>│   ├── root.squashfs<br>│   ├── root.squashfs.docker-digest<br>│   ├── run.json<br>│   └── src.json<br>├── pvr-sdk<br>│   ├── lxc.container.conf<br>│   ├── root.squashfs<br>│   ├── root.squashfs.docker-digest<br>│   ├── run.json<br>│   └── src.json<br>└── storage-mapping.json</pre><p>This is how Pantavisor defines a device with its running containers: :</p><ul><li><strong>BSP</strong>: In embedded systems, a board support package (BSP) is the layer of software containing hardware-specific drivers and other routines that allow a particular operating system to function in a particular hardware environment.</li><li><strong>awconnect</strong>: Pantavisor base platform for the device and where the network configuration is and is based in this <a href="https://gitlab.com/pantacor/pv-platforms/wifi-connect">container</a>.</li><li><strong>pv-avahi</strong>: a container that sends all the information needed to discover the device in the network using pvr device scan and uses the avahi protocol for that. You can see the source container here: <a href="https://gitlab.com/pantacor/pv-platforms/pv-avahi">https://gitlab.com/pantacor/pv-platforms/pv-avahi</a></li><li><strong>pv-sdk</strong>: container with Pantavisor SDK that allows you to maintain the device directly from the device and is the one in charge of managing the ssh connections to the device and to the container inside it.</li></ul><p>Every one of those containers will have inside a <strong>src.json</strong> file that describes the source from where the container was created as well as some of the Pantavisor-specific configurations to run the container.</p><h3>#3 Adding adguard to our device</h3><p>We are going to use the <a href="https://hub.docker.com/r/adguard/adguardhome">adguard docker container</a> as the source for our Pantavisor container (basically an LXC container). In order to add a new container from Docker inside the folder created by the <strong>pvr clone</strong> process, we are going to run the following:</p><pre>pvr app add --from=adguard/adguardhome:latest adguard</pre><p>That creates a new folder called adguard inside the device definition with the following structure inside:</p><pre>adguard/<br>├── lxc.container.conf<br>├── root.squashfs<br>├── root.squashfs.docker-digest<br>├── run.json<br>└── src.json</pre><p>The src.json should look something like this:</p><pre>{<br>  &quot;#spec&quot;: &quot;service-manifest-src@1&quot;,<br>  &quot;template&quot;: &quot;builtin-lxc-docker&quot;,<br>  &quot;args&quot;: {<br>   &quot;PV_RUNLEVEL&quot;: &quot;app&quot;<br>  },<br>  &quot;config&quot;: {},<br>  &quot;docker_name&quot;: &quot;adguard/adguardhome&quot;,<br>  &quot;docker_tag&quot;: &quot;latest&quot;,<br>  &quot;docker_digest&quot;: &quot;adguard/adguardhome@sha256:cd5e6641e969ec8a1df1ed02dc969db49d6cf540055f14346d0d5d42951f75d6&quot;,<br>  &quot;docker_source&quot;: &quot;remote,local&quot;,<br>  &quot;docker_platform&quot;: &quot;linux/arm&quot;,<br>  &quot;persistence&quot;: {}<br>}</pre><h3>#4 Configuring parameters for LXC features and Adguard persistence</h3><p>For now, we will discuss only two parts of this file: <strong>args</strong> and <strong>persistence.</strong></p><ul><li><strong>args: </strong>This is a configuration for the pvr cli tooling that sets up some features for the LXC container.</li><li><strong>persistence</strong>: Configuration for the volumes and persistence for the running container. By default, pvr automatically adds all the volumes defined in the Dockerfile. You can add more volumes here, even if they aren’t defined in the Dockerfile.</li></ul><p>As discussed, pvr adds all the containers by default to the host network namespace. As a result, the first configuration that we need to do is isolate this container inside the LXC bridge network.</p><p>To do that we need to add a couple of new arguments inside the args configuration:</p><ul><li><strong>PV_LXC_NETWORK_TYPE</strong>: This parameter configures <strong>lxc.net.[0].type</strong> and other parameters inside the LXC configuration depending on the value we assign in there.</li><li><strong>PV_LXC_NETWORK_IPV4_ADDRESS</strong>: This is going to be the assigned IP address for the virtual network interface.</li></ul><p>The resulting <strong>args </strong>will be as follows:</p><pre>&quot;args&quot;: {<br>    &quot;PV_LXC_NETWORK_IPV4_ADDRESS&quot;: &quot;10.0.3.20/24&quot;,<br>    &quot;PV_LXC_NETWORK_TYPE&quot;: &quot;veth&quot;,<br>    &quot;PV_RUNLEVEL&quot;: &quot;app&quot;<br>}</pre><p>If you want to know more about what arguments can be used, you can read the source code of our <a href="https://gitlab.com/pantacor/pvr/-/blob/develop/templates/builtin-lxc-docker.go">template to build lxc configurations</a>.</p><p>And for persistence, we need to add the <strong>/opt/adguardhome/work</strong> and the <strong>/opt/adguardhome/conf</strong> folders. Both of these are used by adguard in order to save the configuration of the service.</p><p>The resulting persistence configuration will be:</p><pre>&quot;persistence&quot;: {<br>    &quot;/opt/adguardhome/conf/&quot;: &quot;permanent&quot;,<br>    &quot;/opt/adguardhome/work/&quot;: &quot;permanent&quot;<br>}</pre><h3>#5 Install the Adguard App</h3><p>With the persistence set up and the LXC features enabled, , you are ready to install the Adguard application using <strong>pvr.</strong></p><p>First, check that the finalized src.json looks similar to this:</p><pre>{<br>  &quot;#spec&quot;: &quot;service-manifest-src@1&quot;,<br>  &quot;args&quot;: {<br>    &quot;PV_LXC_NETWORK_IPV4_ADDRESS&quot;: &quot;10.0.3.20/24&quot;,<br>    &quot;PV_LXC_NETWORK_TYPE&quot;: &quot;veth&quot;,<br>    &quot;PV_RUNLEVEL&quot;: &quot;app&quot;<br>  },<br>  &quot;config&quot;: {},<br>  &quot;docker_digest&quot;: &quot;adguard/adguardhome@sha256:cd5e6641e969ec8a1df1ed02dc969db49d6cf540055f14346d0d5d42951f75d6&quot;,<br>  &quot;docker_name&quot;: &quot;adguard/adguardhome&quot;,<br>  &quot;docker_platform&quot;: &quot;linux/arm&quot;,<br>  &quot;docker_source&quot;: &quot;remote,local&quot;,<br>  &quot;docker_tag&quot;: &quot;latest&quot;,<br>  &quot;persistence&quot;: {<br>    &quot;/opt/adguardhome/conf/&quot;: &quot;permanent&quot;,<br>    &quot;/opt/adguardhome/work/&quot;: &quot;permanent&quot;<br>  },<br>  &quot;template&quot;: &quot;builtin-lxc-docker&quot;<br>}</pre><p>If your `src.json` looks fine, let’s proceed with the installation using this command:</p><pre>pvr app install adguard</pre><p>In order to see how Adguard is installed and isolated, push the changes to the device:</p><pre>pvr add &amp;&amp; pvr commit &amp;&amp; pvr post -m &quot;Install adguard into the device&quot;</pre><p>After this you will be able to see a new revision in the device at <a href="https://hub.pantacor.com/">hub.pantacor.com</a>, and similar to this one:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*y5ddVcio0gJZF657" /></figure><p>When the device status is <strong>DONE</strong> or <strong>UPDATED,</strong> the process is finished. The device should be running Adguard now, but because it is isolated, you won’t be able to use the web UI or even use it as DNS.</p><h3>#6 Mapping the Adguard ports with awall</h3><p>The <strong>awconnect</strong> platform uses iptables to manage firewall and is the one that manages to route, and <strong>awall</strong> is a utility to generate iptables and routes based on JSON configuration.</p><p>This is a good <a href="https://wiki.alpinelinux.org/wiki/Zero-To-Awall">guide</a> about how awall works. I will not enter the deepest lands of awall, instead, I will show how to use it for this use case.</p><h3>Configure Awall for Pantavisor</h3><p>First, let’s start by adding this small utility script to run awall without installing it on your computer, and instead, run it from a Docker container. You can see and download the script <a href="https://gist.githubusercontent.com/highercomve/295cf75fb660be4c6b054627c330cb4b/raw/d51989dd5c57c36136814033af6d460db3024bef/awall2pvmwall">here</a>.</p><pre>wget https://gist.githubusercontent.com/highercomve/295cf75fb660be4c6b054627c330cb4b/raw/d51989dd5c57c36136814033af6d460db3024bef/awall2pvmwall &amp;amp;&amp;amp; chmod +x awall2pvmwall</pre><p>That script runs awall for the `awall.json` file in the root of the device folder as well as any `_awall configuration` folder inside any application or platform of the device.</p><p>We are going to create several JSON files to configure awall. Port_forward_example is the folder name for where my device repository lives and also where we will add all the new files for configuration.</p><pre>mkdir -p adguard/_awall<br>touch awall.json<br>touch adguard/_awall/config.json<br>touch bsp/_awall.json</pre><p>The configuration file bsp/_awall.json defines a couple of variables for our device.</p><pre>{<br>   &quot;variable&quot;: {<br>   	&quot;containernet_if&quot;: &quot;lxcbr0&quot;,<br>   	&quot;wan_if&quot;: &quot;eth0&quot;,<br>   	&quot;lan_if&quot;: &quot;wlan0&quot;<br>   }<br>}</pre><p>These are the default values that Pantavisor sets for a Raspberry PI 4. If your Raspberry Pi is connected to the Internet via an ethernet cable, the <strong>wan_if</strong> will be <strong>eth0</strong> and maybe your wifi will be a local area network. In some cases, you may be connected via wifi to an Internet provider and will use the eth0 as an entry point to your local network.</p><p>The<strong> awall.json</strong> configuration file defines the general firewall rules for the device. We are going to add to that file everything that is directly related to the host network namespace.</p><pre>{<br>   &quot;description&quot;: &quot;How to use awall&quot;,<br>   &quot;filter&quot;: [<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;in&quot;: &quot;internet&quot;,<br>           &quot;service&quot;: [<br>               &quot;ping&quot;,<br>               &quot;dns&quot;,<br>               &quot;ssh&quot;,<br>               &quot;dhcp&quot;,<br>               &quot;pvssh&quot;<br>           ]<br>       },<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;in&quot;: &quot;intranet&quot;,<br>           &quot;service&quot;: [<br>               &quot;dns&quot;,<br>               &quot;ping&quot;,<br>               &quot;ssh&quot;,<br>               &quot;dhcp&quot;,<br>               &quot;pvssh&quot;<br>           ]<br>       },<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;in&quot;: &quot;internet&quot;,<br>           &quot;out&quot;: &quot;_fw&quot;,<br>           &quot;service&quot;: [<br>               &quot;ssh&quot;,<br>               &quot;dhcp&quot;,<br>               &quot;pvssh&quot;<br>           ]<br>       },<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;out&quot;: &quot;internet&quot;,<br>           &quot;service&quot;: [<br>               &quot;http&quot;,<br>               &quot;https&quot;<br>           ]<br>       }<br>   ],<br>   &quot;import&quot;: [<br>       &quot;bsp&quot;<br>   ],<br>   &quot;policy&quot;: [<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;out&quot;: &quot;internet&quot;<br>       },<br>       {<br>           &quot;action&quot;: &quot;drop&quot;,<br>           &quot;in&quot;: &quot;internet&quot;<br>       },<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;out&quot;: &quot;intranet&quot;<br>       },<br>       {<br>           &quot;action&quot;: &quot;drop&quot;,<br>           &quot;in&quot;: &quot;intranet&quot;<br>       },<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;in&quot;: &quot;containernet&quot;<br>       },<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;out&quot;: &quot;containernet&quot;<br>       },<br>       {<br>           &quot;action&quot;: &quot;reject&quot;<br>       }<br>   ],<br>   &quot;service&quot;: {<br>       &quot;pvssh&quot;: [<br>           {<br>               &quot;port&quot;: 8222,<br>               &quot;proto&quot;: &quot;tcp&quot;<br>           }<br>       ]<br>   },<br>   &quot;snat&quot;: [<br>       {<br>           &quot;out&quot;: &quot;internet&quot;<br>       }<br>   ],<br>   &quot;zone&quot;: {<br>       &quot;containernet&quot;: {<br>           &quot;iface&quot;: &quot;$containernet_if&quot;<br>       },<br>       &quot;internet&quot;: {<br>           &quot;iface&quot;: &quot;$wan_if&quot;<br>       },<br>       &quot;intranet&quot;: {<br>           &quot;iface&quot;: &quot;$lan_if&quot;<br>       }<br>   }<br>}</pre><p>With this configuration, we open access to some ports of the device:</p><ul><li><strong>ssh</strong>: port 22 to access the <strong>pvr-sdk</strong> container</li><li><strong>pvssh</strong>: port 8222 to enter directly to a container</li><li><strong>dns</strong>: port 53</li><li><strong>ping: </strong>icmp port</li><li><strong>dhcp: </strong>port 67 and 68</li></ul><h3>#7 Redirect traffic to the Adguard IP with DNAT</h3><p>Now let’s redirect everything to the Adguard IP address by using a DNAT configuration. We will need to populate the `<strong>aguard/_awall/config.json` </strong>file with this configuration.</p><pre>{<br>   &quot;description&quot;: &quot;Forward All need it ports to this container&quot;,<br>   &quot;filter&quot;: [<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;dnat&quot;: &quot;10.0.3.20&quot;,<br>           &quot;in&quot;: &quot;internet&quot;,<br>           &quot;service&quot;: &quot;adguard-web&quot;<br>       },<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;dnat&quot;: &quot;10.0.3.20&quot;,<br>           &quot;in&quot;: &quot;intranet&quot;,<br>           &quot;service&quot;: &quot;adguard-web&quot;<br>       },<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;dnat&quot;: &quot;10.0.3.20&quot;,<br>           &quot;in&quot;: &quot;internet&quot;,<br>           &quot;service&quot;: &quot;adguard-dotls&quot;<br>       },<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;dnat&quot;: &quot;10.0.3.20&quot;,<br>           &quot;in&quot;: &quot;intranet&quot;,<br>           &quot;service&quot;: &quot;adguard-dotls&quot;<br>       },<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;dnat&quot;: &quot;10.0.3.20&quot;,<br>           &quot;in&quot;: &quot;internet&quot;,<br>           &quot;service&quot;: &quot;adguard-dnsencrypt&quot;<br>       },<br>       {<br>           &quot;action&quot;: &quot;accept&quot;,<br>           &quot;dnat&quot;: &quot;10.0.3.20&quot;,<br>           &quot;in&quot;: &quot;intranet&quot;,<br>           &quot;service&quot;: &quot;adguard-dnsencrypt&quot;<br>       }<br>   ],<br>   &quot;service&quot;: {<br>       &quot;adguard-web&quot;: [<br>           {<br>               &quot;port&quot;: 3000,<br>               &quot;proto&quot;: &quot;tcp&quot;<br>           },<br>           {<br>               &quot;port&quot;: 3000,<br>               &quot;proto&quot;: &quot;udp&quot;<br>           }<br>       ],<br>       &quot;adguard-dotls&quot;: [<br>           {<br>               &quot;port&quot;: 53,<br>               &quot;proto&quot;: &quot;tcp&quot;<br>           },<br>           {<br>               &quot;port&quot;: 53,<br>               &quot;proto&quot;: &quot;udp&quot;<br>           },<br>           {<br>               &quot;port&quot;: 853,<br>               &quot;proto&quot;: &quot;tcp&quot;<br>           },<br>           {<br>               &quot;port&quot;: 853,<br>               &quot;proto&quot;: &quot;udp&quot;<br>           },<br>           {<br>               &quot;port&quot;: 784,<br>               &quot;proto&quot;: &quot;udp&quot;<br>           },<br>           {<br>               &quot;port&quot;: 8853,<br>               &quot;proto&quot;: &quot;udp&quot;<br>           },<br>           {<br>               &quot;port&quot;: 5443,<br>               &quot;proto&quot;: &quot;tcp&quot;<br>           },<br>           {<br>               &quot;port&quot;: 8853,<br>               &quot;proto&quot;: &quot;udp&quot;<br>           }<br>       ],<br>       &quot;adguard-dnsencrypt&quot;: [<br>           {<br>               &quot;port&quot;: 5443,<br>               &quot;proto&quot;: &quot;tcp&quot;<br>           },<br>           {<br>               &quot;port&quot;: 8853,<br>               &quot;proto&quot;: &quot;udp&quot;<br>           }<br>       ]<br>   }<br>}</pre><p>There, you can see the ports grouped by service. All of those services will be translated from both the WAN and the LAN interface to the container IP.</p><h3>#8 Setting up the port forwarding and viewing the Adguard dashboard</h3><p>Now that we have all the JSON configuration ready, we can run the <strong>awall2pvmwall </strong>script that will create a folder with this structure:</p><pre>_config/<br>└── awconnect<br>	└── etc<br>    	└── iptables<br>        	├── dump<br>        	├── rules.v4<br>        	└── rules.v6</pre><p>With this we can now post this new revision to our device:</p><pre>pvr add .<br>pvr commit<br>pvr post -m &quot;Update awconnect with port forwading to adguard&quot;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*TIQzIG8RcHg5NbJn" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ksoKJC85K_TiUlaD" /></figure><p><strong>NOTE: In here it is important to move from port 80 to 3000</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*eRc17ktGqfwrLhts" /></figure><p>After a couple of more next and next, you will be able to enter the adguard dashboard and use your Raspberry PI as DNS for your entire network and will also be blocking ads,using an upstream DNS via DoT and a lot more..</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*vbv3vTQ7QQeuVpwq" /></figure><p>Here is a<a href="https://kb.adguard.com/general/dns-providers"> list of known DNS providers</a> to choose from.</p><p>You can see the device I built for this guide in <a href="https://hub.pantacor.com/u/highercomve/devices/61147e8af0e0f5000a50d11c">here</a> or you could clone with:</p><pre>pvr clone <a href="https://pvr.pantahub.com/highercomve/port_fordward_example">https://pvr.pantahub.com/highercomve/port_fordward_example</a></pre><p>Now go and play with Pantavisor and containers and may the force be with you.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bd498da69164" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/how-to-do-port-forwarding-on-lxc-with-awall-bd498da69164">How to do Port Forwarding on LXC with Awall</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How I explain to my friends what we do at work]]></title>
            <link>https://itnext.io/how-i-explain-to-my-friends-what-we-do-at-work-f3aa34b18861?source=rss-ea85e17e7579------2</link>
            <guid isPermaLink="false">https://medium.com/p/f3aa34b18861</guid>
            <category><![CDATA[containers]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[front-end-development]]></category>
            <dc:creator><![CDATA[Sergio Marin]]></dc:creator>
            <pubDate>Thu, 25 Jun 2020 22:17:51 GMT</pubDate>
            <atom:updated>2020-06-26T07:08:36.081Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/806/0*FwSExMifj1LW1JJz.png" /></figure><p>Pantahub and Pantavisor could sometimes be a difficult paradigm to understand because, in a way, it changes how we are related to the firmware and software of devices.</p><p>Today, when you don’t do something with an embedded device, maybe an IoT project or home automation with your Raspberry, you will go and flash your device with a Linux distribution and start to install packages and configure all of them via ssh, and if you need to replicate that you will do it all over again. Then if you want to automate that process a little bit you may create a shell script to do all the configuration and installation.</p><p>What happens if you have updated that script and you then need to update that configuration (let’s say you have only two devices), you need to go and run everything on both devices, hopefully via SSH. You can do some kind of more sophisticated thing here, there are a lot of tools for that, but maybe you are running this on a device with very little memory and your plans can fall apart.</p><p>But what we want to do is see devices and their configuration as you see Docker images and Docker containers, but you don’t even need to install and configure the device once. You only need to flash it with Pantahub. and then post revisions that will be deployed on the device. You can even set a device as leader of a pack, and the whole pack will follow the leader revisions automatically.</p><p>Let’s start by understanding all this terminology: devices, revisions, platforms, etc. For that let’s talk more about Pantahub and Pantavisor.</p><h3>What is Pantahub?</h3><p>Pantahub offers cloud-controlled firmware and software management for all kinds of Linux-based connected devices. In addition to this, Pantahub provides a community ecosystem for device makers, operators, and technology enthusiasts to gather around and share their work.</p><p>To use Pantahub, you will need to install Pantavisor on your devices.</p><h3>What is Pantavisor?</h3><p>Pantavisor is the Linux device init system that turns all the runtime into a set of container micro functions. Through Pantavisor, devices become software-defined infrastructure, through Linux container technology. All Pantavisor enabled devices can automatically connect to a Pantahub instance.</p><p>By using these two technologies you will be able to manage your devices and the software in a way similar (similar but not the same) to how you manage applications with docker.</p><p>However, instead of having a container running on the device, the device will run the <strong>PLATFORM</strong> directly on metal, allowing you to have versions of the device. This device state version is called <strong>REVISION</strong>, and inside Pantahub you will have the ability to return to a previous revision and, more importantly, the system is fault-tolerant. That means if you deploy a revision that can’t run correctly on the device, it will start the last revision that worked correctly with that, you can be sure that you won’t lose your device if you configure something in the wrong way or if the software has any kind of problem.</p><h3>How the ecosystem works</h3><p>The ecosystem could look similar to how you see containers on the Docker hub, but instead of containers, you will see devices.</p><p>Here you can see how a device is shown inside Pantahub.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Ffm9BlYvlUQDvNvb.png" /></figure><p>A device in Pantahub represents a physical device, in this example a raspberry pi 3 b+ running several <strong>PLATFORMS</strong>/APPS inside.</p><p>Those <strong>PLATFORMS</strong> are built from a docker container, but inside of the device there isn’t any Docker runtime.</p><p>In this example you see a device running:</p><ul><li><strong>cloudflare</strong>: Platform installed by <a href="https://www.pantahub.com/blog/how-pantahub-and-pantavisor-works/one.apps.pantahub.com">one.apps.pantahub.com</a> to manage the DNS configuration and to set the DNS filters for your WIFI hotspot</li><li><strong>awconnect</strong>: Manages the network and the wifi connections.</li><li><strong>pv-avahi</strong>: Exposes an mDns configuration in order to be able to discover any Pantahub enabled device on the network using the PVR CLI or the Mobile application.</li><li><strong>pvr-sdk</strong>: Manages some of the functionalities of the device like the SSH connection, the ability to follow devices, read user metadata and so much more.</li></ul><p>With this, you can notice how a device is composed of several platforms and those platforms are based on docker containers. But remember there is not any docker container running on the device.</p><p>The device revision is described as a JSON file that is converted to a file system when you clone the device.</p><p>Let me explain with a visual example using the same device.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/766/0*5gBdq7Lzm7hT5Wvi.png" /></figure><p>We call this JSON specification of the device revision state.json</p><p>As you can see the <strong>STATE</strong> is a normalized JSON with the folder structure of the device, where any .json file inside that structure is part of state.json and any other file format is represented by the sha256 of that file and is saved in Pantahub objects (our storage in the cloud)</p><p>When you use pvr clone URL_OF_DEVICE (just like a git clone), you will get the same structure as in the state.json but the files represented as sha256 are going to be downloaded.</p><p>The result is going to be a folder like this one.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*6dlmnnJp5qmR6KI5.png" /></figure><p>Every platform lives in its own folder and all the configuration files live inside of the _config/${PLATFORM} folder</p><p>If you want to dive deep in all the available endpoints you can read the API reference <a href="https://docs.pantahub.com/pantahub-base/devices/">here</a> in the documentation or the swagger documentation <a href="https://api.pantahub.com/swagger/index.html">here</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f3aa34b18861" width="1" height="1" alt=""><hr><p><a href="https://itnext.io/how-i-explain-to-my-friends-what-we-do-at-work-f3aa34b18861">How I explain to my friends what we do at work</a> was originally published in <a href="https://itnext.io">ITNEXT</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>