adding base files

This commit is contained in:
Jake Goldsborough 2025-05-24 08:50:21 -07:00
commit 1ca7ec671d
28 changed files with 780 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
content/blog/_drafts
public

17
config.toml Normal file
View File

@ -0,0 +1,17 @@
# The URL the site will be built for
base_url = "https://jakegoldsborough.com"
title = "Jake Goldsborough"
# Whether to automatically compile all Sass files in the sass directory
compile_sass = false
# Whether to build a search index to be used later on by a JavaScript library
build_search_index = true
[markdown]
# Whether to do syntax highlighting
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
highlight_code = true
[extra]
# Put all your custom variables here

85
content/_index.md Normal file
View File

@ -0,0 +1,85 @@
+++
title = "Home page"
template = "home.html"
+++
<article class="resume">
### Software Engineer | DevOps & Cloud Automation
Versatile Software Engineer with over a decade of experience in software
development, DevOps, and cloud automation. Proven ability to design, develop,
and optimize scalable systems, reducing operational overhead and improving
performance. Passionate about problem-solving, automation, and infrastructure
as code. Experienced in working remotely and collaborating with
cross-functional teams to deliver efficient, maintainable solutions.
#### Experience
**Flashpoint - Software Engineer II (Mid-Level) (2022 - 2024)**
- **Reduced infrastructure setup time from days to hours by leading the Terraform
and Kubernetes pipeline implementation**. Designed reusable Terraform modules
and automated Kubernetes resource creation using Node.js. Initiated Helm
chart development for better deployment management.
- **Eliminated hours of manual work across teams** by enhancing an AngularJS web
application to efficiently manage and connect to cloud-based VMs.
- **Refactored a legacy Java-based API into a modern Node.js service**, reducing
technical debt and simplifying maintenance for future feature requests.
- **Developed CI/CD pipelines and Dockerized applications to support an
AngularJS-to-React migration**, ensuring seamless frontend transitions and
improving local development environments.
- **Debugged and resolved mission-critical issues in cloud-based Linux VMs**,
including optimizing RDP performance and fixing PulseAudio configurations,
enhancing system stability.
- **Mentored junior engineers** and contributed to technical interviews,
fostering a culture of continuous learning and collaboration.
**Infinity Interactive - Software Engineer (2010 - 2020)**
- **Contracted for projects with Fortune 100 and Fortune 500 companies**,
including Shutterstock, Allegra, and Flashpoint.
- **Improved application performance by 75%** by implementing a Redis caching
strategy for a high-traffic application.
- **Designed and deployed a GraphQL API using Node.js and Apollo**, optimizing
backend data retrieval and improving response times.
- **Developed and maintained full-stack applications**, integrating modern
JavaScript frameworks like React and Vue.js.
#### Skills & Technologies
- **Programming**: Node.js, TypeScript, Python, Rust
- **Cloud & Infrastructure**: Terraform, Kubernetes, Google Cloud
- **Frontend**: React, Vue.js, AngularJS
- **DevOps & CI/CD**: Docker, Git, NixOS
- **Databases & Caching**: Postgres, MySQL, Redis, GraphQL
- **Operating Systems**: Linux/Unix (Debian, Ubuntu, NixOS, macOS), BSD
#### Languages
- **English**: Native
- **Swedish**: Basic (Learning)
#### Additional Information
Open to relocation to Europe and remote-friendly opportunities.
Strong advocate for automation, scalable infrastructure, and developer tooling improvements.
Available for interviews and technical discussions upon request.
</article>

View File

@ -0,0 +1,3 @@
+++
title = "2025"
+++

View File

@ -0,0 +1,90 @@
+++
title = "NixOS as a daily driver, part 1/? - What and why?"
date = 2025-05-18
+++
First, it's worth explaining what NixOS is and why I or anyone would want to
run it as a daily driver.
But even before diving into NixOS, we have to talk about Nix.
### Nix
Nix is a package manager that is purely functional and creates reproducible
builds specified in the Nix Expression Language. Nix expressions are functions
that take dependencies as arguments which creates a *derivation* that specifies
a reproducible build environment. Nix then stores the results of the build at
unique address specified by a hash of the complete dependency tree. This
is known as the Nix store and it's immutable which allows atomic upgrades,
rollbacks, and simultaneous installations of packages with different versions.
### NixOS
NixOS is an operating system that is built on top of Nix and the idea of purely
functional package management. Packages are never overwritten once built. If
you change the Nix expression for a package, it will be rebuilt, and stored
under a new address with a new hash, preventing interference with an old version.
NixOS takes this a step further and applies this to configuration. By building
your entire system from a Nix expression, NixOS ensures that your old
configuration is never overwritten which allows for easy rollbacks. One big
caveat of this is the elimination of "global" directores such as `/bin`,
`/lib`, `/usr`, etc. All packages are kept in `/nix/store` under a hashed
address. (One exception is a symlink `/bin/sh` to Bash in the Nix store). There
is a `/etc` for system-wide config but many of those files are symlinks to files
in the Nix store.
Everything in NixOS is built by the Nix package manager. This includes the
kernel, applications, system packages, and configuration.
To configure NixOS, you have a file at `/etc/nixos/configuration.nix`.
You will setup everything from your boot devices to what services you want
to run.
Here is a minimal config that enables sshd:
```
{
boot.loader.grub.device = "/dev/sda";
fileSystems."/".device = "/dev/sda1";
services.sshd.enable = true;
}
```
More on this later though.
### Why?
Now the whys. I think many of them speak for themselves but here are mine:
**Rollbacks**
This one is very big especially when learning about a new, drastically different
OS like Nix. Because old config is never overwritten, you can easily cause a
breaking change without being worried about how to fix it (unless maybe it's
bootloader related). In fact, old configs are listed in the boot menu.
**Reproducible Build Configurations**
Kind of like a rollback but starting from scratch. You can take the
`configuration.nix` file, copy to another machine, rebuild, and you will have
the same applications, services, and configuration as before.
**Ad-hoc shell environments**
In a "Nix shell", you can use any program that is packaged with Nix without
needing to install permanently.
For example, you can run `nix-shell -p git neovim node` and you will be dropped
into a shell with those applications installed. This may take some time
depending on the applications installed.
**It's a new way to think**
Honestly, I just like trying new stuff, especially when it's done in a new way.
Linux has mostly been the same for a long time now, so it's refreshing to see
a new way of doing it that also improves on an already solid OS. I also
have really fallen in love with DevOps/IaC type of work, and NixOS definitely
scratches that itch.
### Next time
In the next post, I will go over how to install NixOS and maybe a bit of the
configuration.

View File

@ -0,0 +1,27 @@
+++
title = "Weekly Summary - 20/52"
date = 2025-05-16
+++
I am starting a weekly summary/reflection series that will be a high level
view of things I have worked on or fixed or just things I want to track or note.
Things I enjoyed:
- deployed this site
- got an old raspberry pi running again
- started back into some rust learnings with blockchain, TUI, and MQTT hacking
[^1] [^2] [^3]
- started working on a walnut utensil rest
- applied to 10 jobs
Things I did not enjoy:
- seemingly bricked my RockPro64 as it won't boot past a black screen. ordered
an adapater to help debug at a lower level.
- rejected from multiple jobs
- felt very confused by some of the rust learnings. makes sense in pieces but
hard to see as the whole picture just yet
- confirmed old fitbit can't be flashed with any open source/non-fitbit software
[^1]: [https://ratatui.rs/concepts/application-patterns/the-elm-architecture/](https://ratatui.rs/concepts/application-patterns/the-elm-architecture/)
[^2]: [https://github.com/veeso/tui-realm/tree/main](https://github.com/veeso/tui-realm/tree/main)
[^3]: [https://github.com/bytebeamio/rumqtt](https://github.com/bytebeamio/rumqtt)

View File

@ -0,0 +1,17 @@
+++
title = "Weekly Summary - 21/52"
date = 2025-05-23
+++
This has been a pretty productive week.
Things that were good:
- RockPro64 boots again! Blog post coming...
- continued Swedish language studies - 165 days
- started work on some API inspection. Another blog post coming...
- figured out nixos/hyprland boot issue
Things that could be better:
- didn't work on much rust
- can't get gitea ssh setup on VPS
- broke wood router part so had to buy new one to work on utensil rest

5
content/blog/_index.md Normal file
View File

@ -0,0 +1,5 @@
+++
title = "Blog"
sort_by = "date"
template = "blog.html"
+++

85
content/resume.md Normal file
View File

@ -0,0 +1,85 @@
+++
title = "Resume"
path = "resume"
+++
<article class="resume">
### Software Engineer | DevOps & Cloud Automation
Versatile Software Engineer with over a decade of experience in software
development, DevOps, and cloud automation. Proven ability to design, develop,
and optimize scalable systems, reducing operational overhead and improving
performance. Passionate about problem-solving, automation, and infrastructure
as code. Experienced in working remotely and collaborating with
cross-functional teams to deliver efficient, maintainable solutions.
#### Experience
**Flashpoint - Software Engineer II (Mid-Level) (2022 - 2024)**
- **Reduced infrastructure setup time from days to hours by leading the Terraform
and Kubernetes pipeline implementation**. Designed reusable Terraform modules
and automated Kubernetes resource creation using Node.js. Initiated Helm
chart development for better deployment management.
- **Eliminated hours of manual work across teams** by enhancing an AngularJS web
application to efficiently manage and connect to cloud-based VMs.
- **Refactored a legacy Java-based API into a modern Node.js service**, reducing
technical debt and simplifying maintenance for future feature requests.
- **Developed CI/CD pipelines and Dockerized applications to support an
AngularJS-to-React migration**, ensuring seamless frontend transitions and
improving local development environments.
- **Debugged and resolved mission-critical issues in cloud-based Linux VMs**,
including optimizing RDP performance and fixing PulseAudio configurations,
enhancing system stability.
- **Mentored junior engineers** and contributed to technical interviews,
fostering a culture of continuous learning and collaboration.
**Infinity Interactive - Software Engineer (2010 - 2020)**
- **Contracted for projects with Fortune 100 and Fortune 500 companies**,
including Shutterstock, Allegra, and Flashpoint.
- **Improved application performance by 75%** by implementing a Redis caching
strategy for a high-traffic application.
- **Designed and deployed a GraphQL API using Node.js and Apollo**, optimizing
backend data retrieval and improving response times.
- **Developed and maintained full-stack applications**, integrating modern
JavaScript frameworks like React and Vue.js.
#### Skills & Technologies
- **Programming**: Node.js, TypeScript, Python, Rust
- **Cloud & Infrastructure**: Terraform, Kubernetes, Google Cloud
- **Frontend**: React, Vue.js, AngularJS
- **DevOps & CI/CD**: Docker, Git, NixOS
- **Databases & Caching**: Postgres, MySQL, Redis, GraphQL
- **Operating Systems**: Linux/Unix (Debian, Ubuntu, NixOS, macOS), BSD
#### Languages
- **English**: Native
- **Swedish**: Basic (Learning)
#### Additional Information
Open to relocation to Europe and remote-friendly opportunities.
Strong advocate for automation, scalable infrastructure, and developer tooling improvements.
Available for interviews and technical discussions upon request.
</article>

176
static/css/base.css Normal file
View File

@ -0,0 +1,176 @@
/* static/style.css */
@font-face {
font-family: "BerkeleyMono";
src: url("/fonts/berkeley-mono/BerkeleyMono-Regular.woff2")
format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "BerkeleyMono";
src: url("/fonts/berkeley-mono/BerkeleyMono-Bold.woff2")
format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "BerkeleyMono";
src: url("/fonts/berkeley-mono/BerkeleyMono-Italic.woff2")
format("woff2");
font-weight: 400;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "BerkeleyMono";
src: url("/fonts/berkeley-mono/BerkeleyMono-BoldItalic.woff2")
format("woff2");
font-weight: 700;
font-style: italic;
font-display: swap;
}
:root {
color-scheme: dark light;
--bg: #282828;
--fg: #ebdbb2;
--red: #fb4934;
--green: #b8bb26;
--yellow: #fabd2f;
--blue: #83a598;
--purple: #d3869b;
--aqua: #8ec07c;
--orange: #d65d0e;
--gray: #a89984;
--font-main: BerkeleyMono, system-ui, sans-serif;
--font-mono: monospace;
--spacing: 1rem;
--max-width: 60rem;
--accent-main: var(--purple);
--accent-comp: var(--green);
}
body.light {
/* Gruvbox Light */
--bg: #ebdbb2;
--fg: #3c3836;
--red: #9d0006;
--green: #79740e;
--yellow: #b57614;
--blue: #076678;
--purple: #8f3f71;
--aqua: #427b58;
--orange: #af3a03;
--gray: #928374;
--accent-main: var(--purple);
--accent-comp: var(--green);
}
body {
background-color: var(--bg);
color: var(--fg);
font-family: var(--font-main);
margin: 0;
line-height: 1.6;
padding: 2rem;
}
a {
color: var(--accent-main);
text-decoration: none;
}
a:hover, a.active {
text-decoration: underline;
}
header, footer {
padding: 1rem 0;
text-align: center;
}
header h1 {
color: var(--accent-main);
font-size: clamp(2.25rem, 7vw, 5rem);
margin-bottom: 0;
a:hover {
text-decoration: none;
}
}
footer {
border-top: 1px solid var(--gray);
border-bottom: none;
margin-top: 2rem;
text-align: center;
font-size: 0.9rem;
}
nav {
display: flex;
justify-content: center;
font-size: clamp(1.5rem, 5vw, 3rem);
a {
font-weight: bold;
flex: 0 auto;
margin: 0 0.5rem;
}
}
main {
max-width: 60ch;
margin: auto;
}
h1, h2, h3, h4, h5, h6 {
color: var(--accent-comp);
margin-top: 2rem;
}
h2 {
font-size: clamp(1.5rem, 5vw, 3rem);
}
h3 {
font-size: clamp(1.25rem, 4vw, 2rem);
}
h4 {
font-size: clamp(1.25rem, 4vw, 2rem);
}
code {
font-family: var(--font-mono);
background-color: #3c3836;
padding: 0.2em 0.4em;
border-radius: 4px;
}
pre code {
display: block;
padding: 1em;
overflow-x: auto;
background-color: #3c3836;
border-radius: 6px;
}
blockquote {
border-left: 4px solid var(--aqua);
padding-left: 1em;
margin-left: 0;
color: var(--gray);
font-style: italic;
}

17
static/css/blog.css Normal file
View File

@ -0,0 +1,17 @@
.blog-posts {
padding-left: 0;
.published {
color: var(--accent-main);
font-weight: bold;
}
.post-item {
margin-bottom: 2rem;
list-style: none;
}
.post-item h3 {
margin-bottom: 0.2rem;
}
}

5
static/css/footnotes.css Normal file
View File

@ -0,0 +1,5 @@
.footnote-definition {
p {
margin-top: 0;
}
}

5
static/css/resume.css Normal file
View File

@ -0,0 +1,5 @@
article.resume {
h1, h2, h3, h4, h5, h6 {
color: var(--orange);
}
}

5
static/css/style.css Normal file
View File

@ -0,0 +1,5 @@
@import url('base.css');
@import url('theme-picker.css');
@import url('resume.css');
@import url('footnotes.css');
@import url('blog.css');

View File

@ -0,0 +1,43 @@
.theme-picker {
position: absolute;
top: 0;
right: 0;
font-size: 1.5rem;
color: var(--fg);
padding-right: var(--spacing);
label {
margin-left: 0.75rem;
cursor: pointer;
user-select: none;
}
input[type="radio"] {
display: none;
}
span {
font-weight: normal;
color: var(--fg);
}
input[value="light"] + span::before {
content: "light";
}
input[value="dark"] + span::before {
content: "dark";
}
input:not(:checked) + span {
text-decoration: underline;
}
input:checked + span {
font-weight: bold;
}
.theme-picker noscript label {
display: inline;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

18
static/js/main.js Normal file
View File

@ -0,0 +1,18 @@
const body = document.body;
const radios = document.querySelectorAll('input[name="theme"]');
function applyTheme(theme) {
body.className = theme;
localStorage.setItem('theme', theme);
}
const stored = localStorage.getItem('theme') || 'dark';
applyTheme(stored);
const selected = document.querySelector(`input[value="${stored}"]`);
if (selected) selected.checked = true;
radios.forEach(radio => {
radio.addEventListener('change', () => {
applyTheme(radio.value);
});
});

View File

@ -0,0 +1,4 @@
<a href="{{ href | safe }}"
{% if href is starting_with("http") %} target="_blank" rel="noopener noreferrer"{% endif %}>
{{ text | safe }}
</a>

62
templates/base.html Normal file
View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>
{% if page.title %}
{{ page.title }} -
{% endif %}
{{ config.title }}
</title>
<link rel="stylesheet" href="{{ get_url(path='css/style.css') }}" />
{# Optional favicon #}
<link rel="icon" href="{{ get_url(path='favicon.ico') }}" />
</head>
<body>
<header>
<div class="theme-picker">
<label aria-label="Dark theme">
<input type="radio" name="theme" value="dark" />
<span aria-hidden="true"></span>
</label>
<label aria-label="Light theme">
<input type="radio" name="theme" value="light" />
<span aria-hidden="true"></span>
</label>
<noscript>
<label aria-label="Dark theme"><span aria-hidden="true">dark</span></label>
<label aria-label="Light theme"><span aria-hidden="true">light</span></label>
</noscript>
</div>
<h1>
<a href="{{ get_url(path='resume') }}">{{ config.title }}</a>
</h1>
<nav>
<a
href="{{ get_url(path='blog') }}"
class="{% if 'blog' in current_path %}active{% endif %}"
>Blog</a>
<span>|</span>
<a
href="{{ get_url(path='resume') }}"
class="{% if 'resume' in current_path %}active{% endif %}"
>Resume</a>
</nav>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>&copy; {{ now() | date(format="%Y") }} {{ config.title }}</p>
</footer>
<script src="/js/main.js"></script>
</body>
</html>

11
templates/blog.html Normal file
View File

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% import "macros.html" as macros %}
{% block content %}
<h2>{{ section.title }}</h2>
<ul class="blog-posts">
{{ macros::list_pages_recursively(section=section) }}
</ul>
{% endblock %}

View File

@ -0,0 +1,22 @@
{% extends "base.html" %}
{% block content %}
<article>
<h1>{{ page.title }}</h1>
<p>
Published {{ page.date | date(format="%B %e, %Y") }} ·
{{ page.reading_time }} min read
</p>
{% if page.taxonomies.tags %}
<p>
Tags:
{% for tag in page.taxonomies.tags %}
<a href="{{ tag.permalink }}">{{ tag.name }}</a>
{% endfor %}
</p>
{% endif %}
{{ page.content | safe }}
</article>
{% endblock %}

7
templates/home.html Normal file
View File

@ -0,0 +1,7 @@
{% extends "base.html" %}
{% block content %}
<section class="homepage">
{{ section.content | safe }}
</section>
{% endblock %}

1
templates/index.html Normal file
View File

@ -0,0 +1 @@
{% extends "base.html" %}

42
templates/macros.html Normal file
View File

@ -0,0 +1,42 @@
{% macro list_pages_recursively(section) %}
{% set posts = [] %}
{% set drafts = [] %}
{# Separate drafts from non-drafts #}
{% for page in section.pages %}
{% if page.draft %}
{% set_global drafts = drafts | concat(with=page) %}
{% else %}
{% set_global posts = posts | concat(with=page) %}
{% endif %}
{% endfor %}
{# Sort non drafts #}
{% set sorted = posts | sort(attribute="date") | reverse %}
{% for page in sorted %}
<li class="post-item">
<h3><a href="{{ page.permalink }}">{{ page.title }}</a></h3>
<div class="meta">
{% if page.date %}
<p>Published on {{ page.date | date(format="%Y-%m-%d") }}</p>
{% endif %}
<p>{{ page.reading_time }} min read</p>
{% if page.description %}
<p class="desc">{{ page.description }}</p>
{% endif %}
</li>
{% endfor %}
{% for subsection_path in section.subsections %}
{% set subsection = get_section(path=subsection_path) %}
{{ self::list_pages_recursively(section=subsection) }}
{% endfor %}
{% for page in drafts %}
<li class="post-item">
<h3><a href="{{ page.permalink }}">{{ page.title }} (draft)</a></h3>
</li>
{% endfor %}
{% endmacro %}

31
templates/page.html Normal file
View File

@ -0,0 +1,31 @@
{% extends 'base.html' %}
{% block content %}
{% if page.components | first == "blog" %}
<article>
<h1>{{ page.title }}</h1>
{% if page.date %}
<p>
Published {{ page.date | date(format="%B %e, %Y") }}
</p>
{% endif %}
<p>
{{ page.reading_time }} min read
</p>
{% if page.taxonomies.tags %}
<p>
Tags:
{% for tag in page.taxonomies.tags %}
<a href="{{ tag.permalink }}">{{ tag.name }}</a>
{% endfor %}
</p>
{% endif %}
{{ page.content | safe }}
</article>
{% else %}
<h2>{{ page.title }}</h2>
{{ page.content | safe }}
{% endif %}
{% endblock %}