Files
site/index.html
2024-01-08 00:26:17 +00:00

15 lines
34 KiB
HTML

<!doctype html><html lang=en data-theme=dark><head><meta name=generator content="Hugo 0.121.1"><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><link rel=preload as=font type=font/woff2 href=/fonts/roboto-slab-latin-400.woff2 crossorigin=anonymous><link rel=preload as=font type=font/woff2 href=/fonts/roboto-slab-latin-700.woff2 crossorigin=anonymous><link rel=preload as=font type=font/woff2 href=/fonts/fira-code-latin-300.woff2 crossorigin=anonymous><link rel=preload as=font type=font/woff2 href=/fonts/fira-code-latin-400.woff2 crossorigin=anonymous><link rel=preload as=font type=font/woff2 href=/fonts/fira-code-latin-700.woff2 crossorigin=anonymous><meta name=robots content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"><title>davegallant.ca — blog</title>
<meta name=description content="This is a space where I share notes about problems and solutions I&amp;rsquo;ve been exploring. Check out the links in the menu bar for more information on how to connect with me."><link rel=canonical href=/><meta name=twitter:card content="summary"><meta name=twitter:title content="Welcome"><meta name=twitter:description content="A retro-looking Hugo theme inspired by gruvbox. The pastel colors are high contrast, easily distinguishable, pleasing to the eye, and feature light and dark color palettes."><meta property="og:title" content="Welcome"><meta property="og:description" content="A retro-looking Hugo theme inspired by gruvbox. The pastel colors are high contrast, easily distinguishable, pleasing to the eye, and feature light and dark color palettes."><meta property="og:type" content="website"><meta property="og:url" content="/"><meta itemprop=name content="Welcome"><meta itemprop=description content="A retro-looking Hugo theme inspired by gruvbox. The pastel colors are high contrast, easily distinguishable, pleasing to the eye, and feature light and dark color palettes."><link rel=alternate type=application/rss+xml href=/index.xml title=davegallant.ca><style>/*! purgecss start ignore */@font-face{font-display:swap;font-family:Roboto Slab;font-style:normal;font-weight:100;src:local("Roboto Slab Thin "),local("Roboto Slab-Thin"),url(/fonts/roboto-slab-latin-100.woff2) format("woff2"),url(/fonts/roboto-slab-latin-100.woff) format("woff")}@font-face{font-display:swap;font-family:Roboto Slab;font-style:normal;font-weight:200;src:local("Roboto Slab Extra Light "),local("Roboto Slab-Extra Light"),url(/fonts/roboto-slab-latin-200.woff2) format("woff2"),url(/fonts/roboto-slab-latin-200.woff) format("woff")}@font-face{font-display:swap;font-family:Roboto Slab;font-style:normal;font-weight:300;src:local("Roboto Slab Light "),local("Roboto Slab-Light"),url(/fonts/roboto-slab-latin-300.woff2) format("woff2"),url(/fonts/roboto-slab-latin-300.woff) format("woff")}@font-face{font-display:swap;font-family:Roboto Slab;font-style:normal;font-weight:400;src:local("Roboto Slab Regular "),local("Roboto Slab-Regular"),url(/fonts/roboto-slab-latin-400.woff2) format("woff2"),url(/fonts/roboto-slab-latin-400.woff) format("woff")}@font-face{font-display:swap;font-family:Roboto Slab;font-style:normal;font-weight:500;src:local("Roboto Slab Medium "),local("Roboto Slab-Medium"),url(/fonts/roboto-slab-latin-500.woff2) format("woff2"),url(/fonts/roboto-slab-latin-500.woff) format("woff")}@font-face{font-display:swap;font-family:Roboto Slab;font-style:normal;font-weight:600;src:local("Roboto Slab SemiBold "),local("Roboto Slab-SemiBold"),url(/fonts/roboto-slab-latin-600.woff2) format("woff2"),url(/fonts/roboto-slab-latin-600.woff) format("woff")}@font-face{font-display:swap;font-family:Roboto Slab;font-style:normal;font-weight:700;src:local("Roboto Slab Bold "),local("Roboto Slab-Bold"),url(/fonts/roboto-slab-latin-700.woff2) format("woff2"),url(/fonts/roboto-slab-latin-700.woff) format("woff")}@font-face{font-display:swap;font-family:Roboto Slab;font-style:normal;font-weight:800;src:local("Roboto Slab ExtraBold "),local("Roboto Slab-ExtraBold"),url(/fonts/roboto-slab-latin-800.woff2) format("woff2"),url(/fonts/roboto-slab-latin-800.woff) format("woff")}@font-face{font-display:swap;font-family:Roboto Slab;font-style:normal;font-weight:900;src:local("Roboto Slab Black "),local("Roboto Slab-Black"),url(/fonts/roboto-slab-latin-900.woff2) format("woff2"),url(/fonts/roboto-slab-latin-900.woff) format("woff")}@font-face{font-display:swap;font-family:Fira Code;font-style:normal;font-weight:300;src:local("Fira Code Light "),local("Fira Code-Light"),url(/fonts/fira-code-latin-300.woff2) format("woff2"),url(/fonts/fira-code-latin-300.woff) format("woff")}@font-face{font-display:swap;font-family:Fira Code;font-style:normal;font-weight:400;src:local("Fira Code Regular "),local("Fira Code-Regular"),url(/fonts/fira-code-latin-400.woff2) format("woff2"),url(/fonts/fira-code-latin-400.woff) format("woff")}@font-face{font-display:swap;font-family:Fira Code;font-style:normal;font-weight:500;src:local("Fira Code Medium "),local("Fira Code-Medium"),url(/fonts/fira-code-latin-500.woff2) format("woff2"),url(/fonts/fira-code-latin-500.woff) format("woff")}@font-face{font-display:swap;font-family:Fira Code;font-style:normal;font-weight:600;src:local("Fira Code SemiBold "),local("Fira Code-SemiBold"),url(/fonts/fira-code-latin-600.woff2) format("woff2"),url(/fonts/fira-code-latin-600.woff) format("woff")}@font-face{font-display:swap;font-family:Fira Code;font-style:normal;font-weight:700;src:local("Fira Code Bold "),local("Fira Code-Bold"),url(/fonts/fira-code-latin-700.woff2) format("woff2"),url(/fonts/fira-code-latin-700.woff) format("woff")}
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;-webkit-text-decoration:underline;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}
/*! purgecss end ignore */
/*! CC BY-SA 3.0 License | https://stackoverflow.com/a/36118384/1154965 */@keyframes blink{50%{opacity:0}to{opacity:1}}
/*! MIT License | github.com/schnerring/hugo-theme-gruvbox */@media (prefers-color-scheme:light){:root{--bg:var(--bg0);--bg0:#fbf1c7;--bg0_h:#f9f5d7;--bg0_s:#f2e5bc;--bg1:#ebdbb2;--bg2:#d5c4a1;--bg3:#bdae93;--bg4:#a89984;--fg:var(--fg1);--fg0:#282828;--fg1:#3c3836;--fg2:#504945;--fg3:#665c54;--fg4:#7c6f64;--gray1:var(--fg4);--gray2:#928374;--red1:#cc241d;--red2:#9d0006;--green1:#98971a;--green2:#797403;--yellow1:#d79921;--yellow2:#b57614;--blue1:#458588;--blue2:#076678;--purple1:#b16286;--purple2:#8f3f71;--aqua1:#689d6a;--aqua2:#427b58;--orange1:#d65d0e;--orange2:#af3a03}:root .light--hidden{display:none}}@media (prefers-color-scheme:dark){:root{--bg:var(--bg0);--bg0:#282828;--bg0_h:#1d2021;--bg0_s:#32302f;--bg1:#3c3836;--bg2:#504945;--bg3:#665c54;--bg4:#7c6f64;--fg:var(--fg1);--fg0:#fbf1c7;--fg1:#ebdbb2;--fg2:#d5c4a1;--fg3:#bdae93;--fg4:#a89984;--gray1:var(--fg4);--gray2:#928374;--red1:#cc241d;--red2:#fb4934;--green1:#98971a;--green2:#b8bb26;--yellow1:#d79921;--yellow2:#fabd2f;--blue1:#458588;--blue2:#83a598;--purple1:#b16286;--purple2:#d3869b;--aqua1:#689d6a;--aqua2:#8ec07c;--orange1:#d65d0e;--orange2:#fe8019}:root .dark--hidden{display:none}}:root{--primary:var(--blue1);--primary-alt:var(--blue2);--font-monospace:"Fira Code","Lucida Console",Monaco,monospace;--font-sans-serif:Verdana,Helvetica,sans-serif;--font-serif:"Roboto Slab",Georgia,serif}html{font-family:Roboto Slab,Georgia,serif;font-family:var(--font-serif);font-size:1rem;scroll-behavior:smooth}body{background:var(--bg);color:var(--fg);line-height:1.675;word-wrap:break-word}strong{letter-spacing:.35px}a{color:inherit;-webkit-text-decoration:none;text-decoration:none}a.link--external:after{content:"\2009↗"}img,video{border:2px solid var(--bg1);height:auto;max-width:100%}figure{display:inline-block}figcaption{color:var(--fg3);font-family:Roboto Slab,Georgia,serif;font-family:var(--font-serif);font-size:.9rem}::-moz-selection{background:var(--bg4);color:var(--fg0)}::selection{background:var(--bg4);color:var(--fg0)}h1,h2,h3,h4,h5,h6{color:var(--fg0);font-family:Fira Code,Lucida Console,Monaco,monospace;font-family:var(--font-monospace);font-weight:300;line-height:1.4}h1 code,h2 code,h3 code,h4 code,h5 code,h6 code{font-size:1em}h2,h3,h4,h5,h6{border-bottom:1px solid var(--bg1)}h1,h2{font-weight:400}h1{font-size:1.875rem}h2{font-size:1.75rem}h3{font-size:1.625rem}@media (min-width:768px){h1{font-size:2.375rem}h2{font-size:2rem}h3{font-size:1.75rem}}h4{font-size:1.5rem}h5{font-size:1.375rem}h6{font-size:1.25rem}table{border-collapse:collapse;margin:2rem 0;table-layout:fixed;width:100%}table,td,th{border:1px solid var(--bg1);padding:.5rem}hr{background:var(--bg1);border:none;height:1px;margin:3rem auto;width:80%}mark{background:var(--yellow1);color:var(--bg0)}abbr{text-underline-offset:.2rem}blockquote,code,kbd,mark,pre{border-radius:.2rem;padding:0 .2em}pre code{padding:0}blockquote,code,kbd,pre,th{background:var(--bg1)}code,kbd,pre,th{font-family:Fira Code,Lucida Console,Monaco,monospace;font-family:var(--font-monospace)}code code,code kbd,kbd code,kbd kbd{background:var(--bg2)}blockquote,pre{padding:1rem}pre{background:var(--bg1)!important;overflow:auto}pre code{background:none}blockquote,blockquote.twitter-tweet{border-left:5px solid var(--primary-alt);margin:.5rem 0}blockquote.twitter-tweet code,blockquote:not(.does-not-exist) code{background:var(--bg2)}blockquote.twitter-tweet p:first-of-type,blockquote:not(.does-not-exist) p:first-of-type{margin-top:0}blockquote.twitter-tweet p:last-of-type,blockquote:not(.does-not-exist) p:last-of-type{margin-bottom:0}blockquote.twitter-tweet{border-color:var(--blue2);color:inherit;font:inherit;font-size:inherit;line-height:inherit}blockquote.twitter-tweet a{color:var(--blue2)}blockquote.twitter-tweet a:hover{color:var(--blue1);-webkit-text-decoration:none!important;text-decoration:none!important}pre::-webkit-scrollbar{height:.5rem;scrollbar-width:auto}pre::-webkit-scrollbar-track{background:var(--bg2);border-radius:.2rem}pre::-webkit-scrollbar-thumb{background:var(--bg4);border-radius:.2rem}.layout{display:grid;grid-template-areas:"header" "main" "footer";grid-template-rows:auto 1fr auto;height:100vh}main{align-items:start;display:grid;grid-area:main;grid-template-areas:"empty content sidebar";grid-template-columns:2fr minmax(0,860px) 2fr}header{background:var(--bg1);grid-area:header}footer{grid-area:footer}footer,main{margin:.5em 1.1em}.content{grid-area:content}.sidebar{display:none;flex-direction:column;grid-area:sidebar;margin-top:3rem;position:sticky;top:2rem}@media (min-width:992px){.sidebar{display:flex}}header{display:grid;font-family:Fira Code,Lucida Console,Monaco,monospace;font-family:var(--font-monospace);font-size:1.125rem;grid-template-areas:"heading search nav theme-toggle";grid-template-columns:auto auto 1fr auto;padding:.75rem}.logo{color:var(--fg0);display:flex;font-weight:700;grid-area:heading}.logo:hover .logo__cursor{animation:blink 1s infinite;opacity:1}.logo__chevron,.logo__cursor{margin-left:.5rem}.logo__cursor{opacity:0}.logo__text{display:none}@media (min-width:768px){.logo__text{display:block}}
/*! purgecss start ignore */.search{display:flex;grid-area:search;margin:0 1rem}#search__text{background:var(--bg2);border:1px solid var(--bg2);border-radius:.2rem;caret-color:var(--fg);color:var(--fg);outline:none;padding:0 .5rem;width:100%}#search__text:hover{border-color:var(--bg3)}#search__text:focus{border-color:var(--bg4)}#search__text::-moz-placeholder{color:var(--fg3)}#search__text::placeholder{color:var(--fg3)}#search__text[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}#search__suggestions{background:var(--bg);border-radius:.2rem;box-shadow:0 .5rem 1rem var(--bg1);font-family:Roboto Slab,Georgia,serif;font-family:var(--font-serif);left:0;margin-top:2rem;position:absolute;width:95vw;z-index:1000}@media (min-width:768px){.search{position:relative}#search__suggestions{width:60vw}}.search__suggestions--hidden{display:none}.search__suggestion-item{border-bottom:1px dashed var(--bg2);display:grid;grid-template-columns:1fr 2fr}.search__suggestion-item:focus,.search__suggestion-item:focus-visible,.search__suggestion-item:hover{background:var(--bg1);cursor:pointer;outline:none}.search__suggestion-item:last-child{border:none}.search__suggestion-description,.search__suggestion-title{margin:1rem 0;padding:0 1rem}.search__suggestion-title{font-weight:700}.search__suggestion-description{border-left:1px solid var(--bg2)}.search__no-results{padding:.75rem}
/*! purgecss end ignore */.theme__toggle{align-items:center;background:none;border:none;color:var(--yellow1);cursor:pointer;display:flex;grid-area:theme-toggle;margin:0 1rem}.theme__toggle:hover{color:var(--yellow2)}.theme__toggle svg{height:28px;width:28px}nav#menu{align-items:center;display:flex;grid-area:nav;justify-content:flex-end}nav#menu .menu__item{color:var(--fg)}nav#menu .menu__item:hover{color:var(--fg3);cursor:pointer}nav#menu ul{list-style:none;margin:0;padding:0}nav#menu ul.menu--horizontal{align-items:center;display:none}nav#menu ul.menu--horizontal li{display:inline-block;margin:0 .75rem}@media (min-width:768px){nav#menu ul.menu--horizontal{display:flex}}nav#menu ul.menu--vertical{background:var(--fg0);bottom:0;margin:0;padding:3rem;position:fixed;right:0;top:0;transform:translate(100%);transition:transform .5s cubic-bezier(.9,0,.1,1);width:50%;z-index:10}nav#menu ul.menu--vertical .menu__item{color:var(--bg1)}nav#menu ul.menu--vertical .menu__item:hover{color:var(--bg4)}nav#menu .menu__burger{display:flex;height:24px;width:24px}nav#menu .menu__burger>*{position:absolute}nav#menu .menu__burger svg{height:inherit;width:inherit;z-index:20}nav#menu .menu__burger svg line{transition-duration:.5s;transition-property:stroke,opacity,transform;transition-timing-function:cubic-bezier(.9,0,.1,1)}nav#menu .menu__burger svg line:first-of-type{transform-origin:center 6px}nav#menu .menu__burger svg line:nth-of-type(2){transform-origin:center 12px}nav#menu .menu__burger svg line:nth-of-type(3){transform-origin:center 18px}nav#menu .menu__burger input{height:inherit;opacity:0;width:inherit;z-index:30}nav#menu .menu__burger input:checked~ul.menu--vertical{transform:none}nav#menu .menu__burger input:checked~svg{stroke:var(--bg1)}nav#menu .menu__burger input:checked~svg line:first-of-type{transform:translateY(6px) rotate(45deg)}nav#menu .menu__burger input:checked~svg line:nth-of-type(2){opacity:0;transform:scale(.2)}nav#menu .menu__burger input:checked~svg line:nth-of-type(3){transform:translateY(-6px) rotate(-45deg)}@media (min-width:768px){nav#menu .menu__burger{display:none}}.sidebar{font-family:Fira Code,Lucida Console,Monaco,monospace;font-family:var(--font-monospace);margin-left:auto;margin-right:auto;max-width:350px;padding-left:2.5rem}.sidebar hr{margin:1.5rem auto}.sidebar svg{fill:var(--fg)}.sidebar__heading{font-size:1.3rem}aside.toc a{color:var(--blue2);color:var(--primary-alt)}aside.toc a:hover{color:var(--blue1);color:var(--primary)}aside.toc ul{list-style:none;margin:0;padding:0}aside.toc ul ul{font-size:.9rem;margin-left:.5rem}aside.toc ul li{line-height:1.1}aside.toc ul li a{display:block;padding:.2rem 0}.jr-basics__image{background:var(--bg1);border:2px solid var(--bg2)}.jr-basics__summary{color:var(--fg3);font-family:Roboto Slab,Georgia,serif;font-family:var(--font-serif);margin:.75rem 0}.jr-basics__profile a:hover{color:var(--fg3)}.jr-basics__profile a:hover svg{fill:var(--fg3)}.content-section,.post{border-bottom:2px dotted var(--bg1);padding:2rem 0}.post figure,.post img:not(figure img),.post video:not(figure video){box-sizing:border-box;margin:.5rem 0}.post-content__read-more,.post-header{font-family:Fira Code,Lucida Console,Monaco,monospace;font-family:var(--font-monospace)}.post-meta__author{font-weight:700}.post-content{margin:1.3rem 0}.post-content__read-more{margin-top:1.3rem}.post-content a,.post-content__read-more,.post-header a{color:var(--blue2);color:var(--primary-alt)}.post-content a:hover,.post-header a:hover{color:var(--blue1);color:var(--primary)}.post-tags{align-items:center;display:flex;flex-wrap:wrap;gap:.9rem;margin:1rem 0}.post-tag{font-size:.9rem;line-height:1}.post-tag:before{content:"#"}.post-heading__anchor{display:none}h1:hover .post-heading__anchor,h2:hover .post-heading__anchor,h3:hover .post-heading__anchor,h4:hover .post-heading__anchor,h5:hover .post-heading__anchor,h6:hover .post-heading__anchor{display:inline-block}</style><link rel=preload href="/css/non-critical.9214dbb3cce2c661a6e5356aef8f0893fa89801812e469e054a8858124096266eebf2c796b034928c657e085be8db512a997d44ac204174f066dac9326c7ab84.css" as=style onload='this.onload=null,this.rel="stylesheet"'><link id=prism-dark rel=preload href=/prism-themes/prism-gruvbox-dark.min.54aecc64074623a4f9898544dcbdab9e804f1560ef0b38f4cf8e10fcaaf72264e798cb407c601aca6ecd833ec4eb93d66535581f18d45ba202cf848b70dbc332.css as=style onload='this.onload=null,this.rel="stylesheet"'><link id=prism-light rel=preload href=/prism-themes/prism-gruvbox-light.min.42a221741efe997fcc94187c39d63c555560678789ac9ca856c74a5f0ddb2aa6c50d38b2ffbecc7a99038cbbd2efa99746e862267f781c559e0cfec10b88a5fc.css as=style onload='this.onload=null,this.rel="stylesheet"' disabled><noscript><link rel=stylesheet href=/prism-themes/prism-gruvbox-dark.min.54aecc64074623a4f9898544dcbdab9e804f1560ef0b38f4cf8e10fcaaf72264e798cb407c601aca6ecd833ec4eb93d66535581f18d45ba202cf848b70dbc332.css><link rel=stylesheet href="/css/non-critical.9214dbb3cce2c661a6e5356aef8f0893fa89801812e469e054a8858124096266eebf2c796b034928c657e085be8db512a997d44ac204174f066dac9326c7ab84.css"></noscript><script>(()=>{function n(){if(window.matchMedia)return window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark"}function e(e){let t=document.getElementById("prism-dark"),n=document.getElementById("prism-light");t.toggleAttribute("disabled",e==="light"),n.toggleAttribute("disabled",e==="dark")}function s(e){if(document.querySelector(".utterances-frame")){let n=document.querySelector(".utterances-frame");var t={type:"set-theme",theme:e=="dark"?"gruvbox-dark":"github-light"};n.contentWindow.postMessage(t,"https://utteranc.es")}}var t=n();t&&e(t),window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",t=>{let n=t.matches?"dark":"light";e(n),s(n)})})()</script><link rel=apple-touch-icon sizes=180x180 href=/apple-touch-icon.png><link rel=icon type=image/png sizes=32x32 href=/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=/favicon-16x16.png><link rel=manifest href=/site.webmanifest><link rel=mask-icon href=/safari-pinned-tab.svg color=#282828><meta name=msapplication-TileColor content="#282828"><meta name=theme-color content="#282828"></head><body><div class=layout><header><a class=logo href=/><div class=logo__text>davegallant.ca</div><div class=logo__chevron>></div><div class=logo__cursor></div></a><div class=search><input id=search__text type=search placeholder=Search... aria-label=Search autocomplete=off><div id=search__suggestions class=search__suggestions--hidden></div></div><nav id=menu><ul class=menu--horizontal><li class=menu__item><a href=/>Posts</a></li><li class=menu__item><a href=/about>About</a></li><li class=menu__item><a href=/index.xml>RSS</a></li></ul><div class=menu__burger><input class=menu__item type=checkbox aria-label="Open main menu"><svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-menu-2" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentcolor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 6h16"/><path d="M4 12h16"/><path d="M4 18h16"/></svg><ul class=menu--vertical><li><a class=menu__item href=/>Posts</a></li><li><a class=menu__item href=/about>About</a></li><li><a class=menu__item href=/index.xml>RSS</a></li></ul></div></nav></header><main><div class=content><div class=content-section><h1>Welcome</h1><div><p>This is a space where I share notes about problems and solutions I&rsquo;ve been exploring. Check out the links in the menu bar for more information on how to connect with me.</p></div></div><article class=post><div class=post-header><h2><a href=/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/>Setting up Gitea Actions with Tailscale</a></h2><div class=post-meta><span>2023-12-10</span><span> by </span><span class=post-meta__author>Dave Gallant</span><div class=post-tags><a class=post-tag href=/tags/gitea>gitea</a><a class=post-tag href=/tags/gitea-actions>gitea&#8209;actions</a><a class=post-tag href=/tags/github-actions>github&#8209;actions</a><a class=post-tag href=/tags/tailscale>tailscale</a><a class=post-tag href=/tags/self-hosted>self&#8209;hosted</a></div></div></div><div class=post-content><p>In this post I&rsquo;ll go through the process of setting up Gitea Actions and <a href=https://tailscale.com/ class=link--external target=_blank rel=noreferrer>Tailscale</a>, unlocking a simple and secure way to automate workflows.</p><div class=post-content__read-more><a href=/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/>Read more ></a></div></div></article><article class=post><div class=post-header><h2><a href=/blog/2023/05/22/using-aks-and-socks-to-connect-to-a-private-azure-db/>Using AKS and SOCKS to connect to a private Azure DB</a></h2><div class=post-meta><span>2023-05-22</span><span> by </span><span class=post-meta__author>Dave Gallant</span><div class=post-tags><a class=post-tag href=/tags/aks>aks</a><a class=post-tag href=/tags/aws>aws</a><a class=post-tag href=/tags/azure>azure</a><a class=post-tag href=/tags/bastion>bastion</a><a class=post-tag href=/tags/cloud-sql-proxy>cloud&#8209;sql&#8209;proxy</a><a class=post-tag href=/tags/database>database</a><a class=post-tag href=/tags/eks>eks</a><a class=post-tag href=/tags/k8s>k8s</a><a class=post-tag href=/tags/kubectl-plugin-socks5-proxy>kubectl&#8209;plugin&#8209;socks5&#8209;proxy</a><a class=post-tag href=/tags/proxy>proxy</a><a class=post-tag href=/tags/socat>socat</a><a class=post-tag href=/tags/socks>socks</a></div></div></div><div class=post-content><p>I ran into a roadblock recently where I wanted to be able to conveniently connect to a managed postgres database within Azure that was not running on public subnets. And by conveniently, I mean that I&rsquo;d rather not have to spin up an ephemeral virtual machine running in the same network and proxy the connection, and I&rsquo;d like to use a local client (preferably with a GUI). After several web searches, it became evident that Azure does not readily provide much tooling to support this.</p><div class=post-content__read-more><a href=/blog/2023/05/22/using-aks-and-socks-to-connect-to-a-private-azure-db/>Read more ></a></div></div></article><article class=post><div class=post-header><h2><a href=/blog/2022/12/10/watching-youtube-in-private/>Watching YouTube in private</a></h2><div class=post-meta><span>2022-12-10</span><span> by </span><span class=post-meta__author>Dave Gallant</span><div class=post-tags><a class=post-tag href=/tags/invidious>invidious</a><a class=post-tag href=/tags/youtube>youtube</a><a class=post-tag href=/tags/yewtu.be>yewtu.be</a><a class=post-tag href=/tags/tailscale>tailscale</a><a class=post-tag href=/tags/privacy>privacy</a><a class=post-tag href=/tags/self-hosted>self&#8209;hosted</a></div></div></div><div class=post-content><p>I recently stumbled upon <a href=https://yewtu.be class=link--external target=_blank rel=noreferrer>yewtu.be</a> and found it intriguing. It not only allows you to watch YouTube without <em>being on YouTube</em>, but it also allows you to create an account and subscribe to channels without a Google account. What sort of wizardry is going on under the hood? It turns out that it&rsquo;s a hosted instance of <a href=https://invidious.io/ class=link--external target=_blank rel=noreferrer>invidious</a>.</p><div class=post-content__read-more><a href=/blog/2022/12/10/watching-youtube-in-private/>Read more ></a></div></div></article><article class=post><div class=post-header><h2><a href=/blog/2022/04/02/virtualizing-my-router-with-pfsense/>Virtualizing my router with pfSense</a></h2><div class=post-meta><span>2022-04-02</span><span> by </span><span class=post-meta__author>Dave Gallant</span><div class=post-tags><a class=post-tag href=/tags/pfsense>pfsense</a><a class=post-tag href=/tags/router>router</a><a class=post-tag href=/tags/openwrt>openwrt</a><a class=post-tag href=/tags/router-on-a-stick>router&#8209;on&#8209;a&#8209;stick</a><a class=post-tag href=/tags/proxmox>proxmox</a><a class=post-tag href=/tags/vlan>vlan</a><a class=post-tag href=/tags/self-hosted>self&#8209;hosted</a></div></div></div><div class=post-content><p>My aging router has been running <a href=https://en.wikipedia.org/wiki/OpenWrt class=link--external target=_blank rel=noreferrer>OpenWrt</a> for years and for the most part has been quite reliable. OpenWrt is an open-source project used on embedded devices to route network traffic. It supports many different configurations and there exists a <a href=https://openwrt.org/packages/index/start class=link--external target=_blank rel=noreferrer>large index of packages</a>. Ever since I&rsquo;ve connected some standalone wireless access points, I&rsquo;ve had less of a need for an off-the-shelf all-in-one wireless router combo. I&rsquo;ve also recently been experiencing instability with my router (likely the result of a combination of configuration tweaking and firmware updating). OpenWrt has served me well, but it is time to move on!</p><div class=post-content__read-more><a href=/blog/2022/04/02/virtualizing-my-router-with-pfsense/>Read more ></a></div></div></article><article class=post><div class=post-header><h2><a href=/blog/2022/03/13/backing-up-gmail-with-synology/>Backing up gmail with Synology</a></h2><div class=post-meta><span>2022-03-13</span><span> by </span><span class=post-meta__author>Dave Gallant</span><div class=post-tags><a class=post-tag href=/tags/synology>synology</a><a class=post-tag href=/tags/gmail>gmail</a><a class=post-tag href=/tags/backup>backup</a><a class=post-tag href=/tags/ransomware>ransomware</a></div></div></div><div class=post-content><p>I&rsquo;ve used gmail since the beta launched touting a whopping 1GB of storage. I thought this was a massive leap in email technology at the time. I was lucky enough to get an invite fairly quickly. Not suprisingly, I have many years of emails, attachments, and photos. I certainly do not want to lose the content of many of these emails. Despite the redundancy of the data that Google secures, I still feel better retaining a copy of this data on my own physical machines.</p><div class=post-content__read-more><a href=/blog/2022/03/13/backing-up-gmail-with-synology/>Read more ></a></div></div></article><article class=post><div class=post-header><h2><a href=/blog/2021/11/14/running-k3s-in-lxc-on-proxmox/>Running K3s in LXC on Proxmox</a></h2><div class=post-meta><span>2021-11-14</span><span> by </span><span class=post-meta__author>Dave Gallant</span><div class=post-tags><a class=post-tag href=/tags/k3s>k3s</a><a class=post-tag href=/tags/proxmox>proxmox</a><a class=post-tag href=/tags/lxc>lxc</a><a class=post-tag href=/tags/self-hosted>self&#8209;hosted</a></div></div></div><div class=post-content>It has been a while since I&rsquo;ve actively used Kubernetes and wanted to explore the evolution of tools such as Helm and Tekton. I decided to deploy K3s, since I&rsquo;ve had success with deploying it on resource-contrained Raspberry Pis in the past. I thought that this time it&rsquo;d be convenient to have K3s running in a LXC container on Proxmox. This would allow for easy snapshotting of the entire Kubernetes deployment.<div class=post-content__read-more><a href=/blog/2021/11/14/running-k3s-in-lxc-on-proxmox/>Read more ></a></div></div></article><article class=post><div class=post-header><h2><a href=/blog/2021/10/11/replacing-docker-with-podman-on-macos-and-linux/>Replacing docker with podman on macOS (and Linux)</a></h2><div class=post-meta><span>2021-10-11</span><span> by </span><span class=post-meta__author>Dave Gallant</span><div class=post-tags><a class=post-tag href=/tags/docker>docker</a><a class=post-tag href=/tags/podman>podman</a><a class=post-tag href=/tags/containers>containers</a></div></div></div><div class=post-content><p>There are a number of reasons why you might want to replace docker, especially on macOS. The following feature bundled in Docker Desktop might have motivated you enough to consider replacing docker:</p><div class=post-content__read-more><a href=/blog/2021/10/11/replacing-docker-with-podman-on-macos-and-linux/>Read more ></a></div></div></article><article class=post><div class=post-header><h2><a href=/blog/2021/09/17/automatically-rotating-aws-access-keys/>Automatically rotating AWS access keys</a></h2><div class=post-meta><span>2021-09-17</span><span> by </span><span class=post-meta__author>Dave Gallant</span><div class=post-tags><a class=post-tag href=/tags/aws>aws</a><a class=post-tag href=/tags/python>python</a><a class=post-tag href=/tags/security>security</a><a class=post-tag href=/tags/aws-vault>aws&#8209;vault</a></div></div></div><div class=post-content>Rotating credentials is a security best practice. This morning, I read a question about automatically rotating AWS Access Keys without having to go through the hassle of navigating the AWS console. There are some existing solutions already, but I decided to write a script since it was incredibly simple. The script could be packed up as a systemd/launchd service to continually rotate access keys in the background.
In the longer term, migrating my local workflows to aws-vault seems like a more secure solution.<div class=post-content__read-more><a href=/blog/2021/09/17/automatically-rotating-aws-access-keys/>Read more ></a></div></div></article><article class=post><div class=post-header><h2><a href=/blog/2021/09/08/why-i-threw-out-my-dotfiles/>Why I threw out my dotfiles</a></h2><div class=post-meta><span>2021-09-08</span><span> by </span><span class=post-meta__author>Dave Gallant</span><div class=post-tags><a class=post-tag href=/tags/nix>nix</a><a class=post-tag href=/tags/dotfiles>dotfiles</a><a class=post-tag href=/tags/home-manager>home&#8209;manager</a></div></div></div><div class=post-content><p>Over the years I have collected a number of dotfiles that I have shared across both Linux and macOS machines (<code>~/.zshrc</code>, <code>~/.config/git/config</code>, <code>~/.config/tmux/tmux.conf</code>, etc). I have tried several different ways to manage them, including <a href=https://www.atlassian.com/git/tutorials/dotfiles class=link--external target=_blank rel=noreferrer>bare git repos</a> and utilities such as <a href=https://www.gnu.org/software/stow/ class=link--external target=_blank rel=noreferrer>GNU Stow</a>. These solutions work well enough, but I have since found what I would consider a much better solution for organizing user configuration: <a href=https://github.com/nix-community/home-manager class=link--external target=_blank rel=noreferrer>home-manager</a>.</p><div class=post-content__read-more><a href=/blog/2021/09/08/why-i-threw-out-my-dotfiles/>Read more ></a></div></div></article><article class=post><div class=post-header><h2><a href=/blog/2021/09/06/what-to-do-with-a-homelab/>What to do with a homelab</a></h2><div class=post-meta><span>2021-09-06</span><span> by </span><span class=post-meta__author>Dave Gallant</span><div class=post-tags><a class=post-tag href=/tags/self-hosted>self&#8209;hosted</a><a class=post-tag href=/tags/proxmox>proxmox</a><a class=post-tag href=/tags/tailscale>tailscale</a></div></div></div><div class=post-content><p>A homelab can be an inexpensive way to host a multitude of internal/external services and learn <em>a lot</em> in the process.</p><div class=post-content__read-more><a href=/blog/2021/09/06/what-to-do-with-a-homelab/>Read more ></a></div></div></article><div class=pagination><span class="pagination__button pagination__button--next"><a href=/page/2/>Older Posts ></a></span></div></div><div class=sidebar></div></main><footer><div class=copyright>Dave Gallant</div></footer><script src=/js/main.4be06c129d6a89e60a661c6ac8c8e0434d58fb0fa2f685f85e2c306aca62adc5e77e7c63cb1c8a2cc5794ea42927281cf868514bcdce21ddf23dc3520e6743e7.js></script><script src=/js/flexsearch.b3edc337cff95a8fdfb52ccd423bce6c5adfe56f8d5b5beb2883c5a1471d061e65d0e2e990181fb3ca51012b62d5bf7ab75e785bd548e46f24875c2f9f434320.js></script><script defer src=https://static.cloudflareinsights.com/beacon.min.js data-cf-beacon='{"token": "b96799f53f9940dca6f660e6052ba009"}'></script></div></body></html>