Compare commits

...

5 Commits

Author SHA1 Message Date
Leopere 3c35c5eba0
Rewrite ScanSnap story with natural, engaging narrative
ci/woodpecker/push/woodpecker Pipeline was successful Details
- Transformed from technical documentation to compelling project story
- Added buildersclub.ca member access link (http://192.168.1.119:9876)
- Included real-world problem solving and decision-making narrative
- Emphasized practical impact and time savings
- Maintained technical depth while improving readability
- Added 'Lessons Learned' section for authenticity
2025-10-19 15:58:20 -04:00
Leopere cbb5a04563
Merge branch 'main' of git.nixc.us:colin/resume
ci/woodpecker/push/woodpecker Pipeline was successful Details
2025-10-19 15:35:12 -04:00
Colin 8f35aa626c
Update homepage: change 'lurked' to 'idled' and remove car purchase reference
ci/woodpecker/push/woodpecker Pipeline was successful Details
2025-10-15 21:24:53 -04:00
Colin 82bc0eb349
build: trigger CI build 20251015-203646
ci/woodpecker/push/woodpecker Pipeline was successful Details
2025-10-15 20:36:46 -04:00
Colin 7e06e335a9
dev: add docker compose for caddy, map 8081; add story placeholders (.md); update SRI via script; local compose run instructions 2025-10-15 16:58:31 -04:00
45 changed files with 455 additions and 86 deletions

View File

@ -93,4 +93,5 @@ This site is designed to meet WCAG 2.1 Level AA+ standards. Key accessibility fe
## License ## License
ISC © Colin Knapp ISC © Colin Knapp # Build trigger Wed Oct 15 20:36:46 EDT 2025
# Build trigger Wed Oct 15 20:36:46 EDT 2025

13
docker-compose.yml Normal file
View File

@ -0,0 +1,13 @@
version: "3.9"
services:
resume:
image: caddy:2-alpine
container_name: resume-caddy
working_dir: /srv
volumes:
- ./docker/resume:/srv:ro
ports:
- "8081:8080"
command: ["caddy", "run", "--config", "/srv/Caddyfile.local"]
restart: unless-stopped

View File

@ -10,7 +10,7 @@
<link rel="stylesheet" href="tool-styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU="> <link rel="stylesheet" href="tool-styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=">
<link rel="stylesheet" href="csv-tool-fix.css?v=2" integrity="sha256-5oTxos9Qxwhor3qIwHSM12YyIZi5E+tHuFdYER0hXoI="> <link rel="stylesheet" href="csv-tool-fix.css?v=2" integrity="sha256-5oTxos9Qxwhor3qIwHSM12YyIZi5E+tHuFdYER0hXoI=">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E="></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA="></script>
<script src="../utils.js" integrity="sha256-ryQsJ+aghKKD/CeXgx8jtsnZT3Epp3EjIw8RyHIq544="></script> <script src="../utils.js" integrity="sha256-ryQsJ+aghKKD/CeXgx8jtsnZT3Epp3EjIw8RyHIq544="></script>
<style> <style>
/* Additional inline styles to fix layout */ /* Additional inline styles to fix layout */

View File

@ -102,22 +102,22 @@ document.addEventListener('DOMContentLoaded', function() {
const link = document.getElementById('nav-viperwire'); const link = document.getElementById('nav-viperwire');
if (link) link.classList.add('active'); if (link) link.classList.add('active');
} else if (currentPath.includes('fawe-plotsquared.html')) { } else if (currentPath.includes('fawe-plotsquared.html')) {
const link = document.getElementById('nav-fawe'); const link = document.getElementById('nav-faweplotsquared');
if (link) link.classList.add('active'); if (link) link.classList.add('active');
} else if (currentPath.includes('healthcare-platform.html')) { } else if (currentPath.includes('healthcare-platform.html')) {
const link = document.getElementById('nav-healthcare'); const link = document.getElementById('nav-healthcareplatform');
if (link) link.classList.add('active'); if (link) link.classList.add('active');
} else if (currentPath.includes('wordpress-security.html')) { } else if (currentPath.includes('wordpress-security.html')) {
const link = document.getElementById('nav-wordpress'); const link = document.getElementById('nav-wordpresssecurity');
if (link) link.classList.add('active'); if (link) link.classList.add('active');
} else if (currentPath.includes('airport-dns.html')) { } else if (currentPath.includes('airport-dns.html')) {
const link = document.getElementById('nav-airport'); const link = document.getElementById('nav-airportdns');
if (link) link.classList.add('active'); if (link) link.classList.add('active');
} else if (currentPath.includes('nitric-leadership.html')) { } else if (currentPath.includes('nitric-leadership.html')) {
const link = document.getElementById('nav-nitric'); const link = document.getElementById('nav-nitricleadership');
if (link) link.classList.add('active'); if (link) link.classList.add('active');
} else if (currentPath.includes('open-source-success.html')) { } else if (currentPath.includes('open-source-success.html')) {
const link = document.getElementById('nav-opensource'); const link = document.getElementById('nav-opensourcesuccess');
if (link) link.classList.add('active'); if (link) link.classList.add('active');
} }
} else if (currentPath.includes('/one-pager-tools/')) { } else if (currentPath.includes('/one-pager-tools/')) {

View File

@ -8,7 +8,7 @@
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU="> <link rel="stylesheet" href="styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=">
<script src="theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script> <script src="theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script>
<script src="includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E="></script> <script src="includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -8,7 +8,7 @@
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU="> <link rel="stylesheet" href="styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=">
<script src="theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script> <script src="theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script>
<script src="includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E="></script> <script src="includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->
@ -249,7 +249,7 @@
</p> </p>
<p><strong>Key Contributions:</strong></p> <p><strong>Key Contributions:</strong></p>
<ul> <ul>
<li>Lurked in IRC chat rooms of various plugin and software developers to assess reliable open source projects for early career dependencies.</li> <li>Idled in IRC chat rooms of various plugin and software developers to assess reliable open source projects for early career dependencies.</li>
<li>Offered CI/CD services and Java Maven repository hosting to open source developers, building goodwill and reciprocal support networks.</li> <li>Offered CI/CD services and Java Maven repository hosting to open source developers, building goodwill and reciprocal support networks.</li>
<li>Utilized Jenkins and GitLab CI/CD for streamlined workflows, leveraging a robust toolchain for rapid development.</li> <li>Utilized Jenkins and GitLab CI/CD for streamlined workflows, leveraging a robust toolchain for rapid development.</li>
<li>Managed complex systems and ensured WCAG 2.0 AA accessibility standards.</li> <li>Managed complex systems and ensured WCAG 2.0 AA accessibility standards.</li>
@ -294,7 +294,7 @@
<li>Bootstrapped the company from zero to an estimated $4M gross revenue with strong profit margins.</li> <li>Bootstrapped the company from zero to an estimated $4M gross revenue with strong profit margins.</li>
<li>Utilized Kanban/Trello to coordinate distributed teams and project workflows across multiple timezones.</li> <li>Utilized Kanban/Trello to coordinate distributed teams and project workflows across multiple timezones.</li>
</ul> </ul>
<p><strong>Impact:</strong> Transformed Nitric Concepts into a thriving multinational entity through prolific and efficient development, while mentoring Andrew Karvelis from side project earnings to being able to purchase a Corvette C7 in cash from one month's disposable income.</p> <p><strong>Impact:</strong> Transformed Nitric Concepts into a thriving multinational entity through prolific and efficient development, while mentoring Andrew Karvelis from side project earnings to significant revenue growth and financial success.</p>
<p><a href="stories/nitric-leadership.html" class="read-more">Read more about my leadership at Nitric Concepts &rarr;</a></p> <p><a href="stories/nitric-leadership.html" class="read-more">Read more about my leadership at Nitric Concepts &rarr;</a></p>
</article> </article>

View File

@ -0,0 +1,62 @@
// Load same-named .md and render into .story-content using Marked. No fallbacks.
(function () {
function getMarkdownPath() {
var parts = (window.location.pathname || '').split('/');
var last = parts[parts.length - 1] || '';
if (!last) return '';
return last.replace(/\.html?$/i, '.md');
}
function loadMarked(callback) {
if (window.marked && typeof window.marked.parse === 'function') {
callback();
return;
}
var script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
script.async = true;
script.onload = callback;
document.head.appendChild(script);
}
function renderMarkdown(container, text) {
if (!window.marked || typeof window.marked.parse !== 'function') {
container.innerHTML = '';
return;
}
try {
if (typeof window.marked.setOptions === 'function') {
window.marked.setOptions({ gfm: true, breaks: true });
}
container.innerHTML = window.marked.parse(text);
} catch (_) {
container.innerHTML = '';
}
}
function init() {
var container = document.querySelector('.story-content');
if (!container) return;
// No fallback: clear immediately
container.innerHTML = '';
var mdPath = getMarkdownPath();
if (!mdPath) return;
loadMarked(function () {
fetch(mdPath, { cache: 'no-cache' })
.then(function (res) { if (!res.ok) throw new Error('md'); return res.text(); })
.then(function (text) { renderMarkdown(container, text); })
.catch(function () { container.innerHTML = ''; });
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();

View File

@ -10,7 +10,7 @@
<link rel="stylesheet" href="tool-styles.css?v=2" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU="> <link rel="stylesheet" href="tool-styles.css?v=2" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=">
<link rel="stylesheet" href="csv-tool-fix.css?v=2" integrity="sha256-5oTxos9Qxwhor3qIwHSM12YyIZi5E+tHuFdYER0hXoI="> <link rel="stylesheet" href="csv-tool-fix.css?v=2" integrity="sha256-5oTxos9Qxwhor3qIwHSM12YyIZi5E+tHuFdYER0hXoI=">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E="></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA="></script>
<script src="../utils.js" integrity="sha256-ryQsJ+aghKKD/CeXgx8jtsnZT3Epp3EjIw8RyHIq544="></script> <script src="../utils.js" integrity="sha256-ryQsJ+aghKKD/CeXgx8jtsnZT3Epp3EjIw8RyHIq544="></script>
<style> <style>
/* Additional inline styles to fix layout */ /* Additional inline styles to fix layout */

View File

@ -9,7 +9,7 @@
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<link rel="stylesheet" href="tool-styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU="> <link rel="stylesheet" href="tool-styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<script src="../utils.js" integrity="sha256-ryQsJ+aghKKD/CeXgx8jtsnZT3Epp3EjIw8RyHIq544=" crossorigin="anonymous"></script> <script src="../utils.js" integrity="sha256-ryQsJ+aghKKD/CeXgx8jtsnZT3Epp3EjIw8RyHIq544=" crossorigin="anonymous"></script>
<!-- Add tool-specific scripts here --> <!-- Add tool-specific scripts here -->
</head> </head>

View File

@ -10,7 +10,7 @@
<link rel="stylesheet" href="tool-styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU="> <link rel="stylesheet" href="tool-styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script>
<script src="../utils.js" integrity="sha256-ryQsJ+aghKKD/CeXgx8jtsnZT3Epp3EjIw8RyHIq544="></script> <script src="../utils.js" integrity="sha256-ryQsJ+aghKKD/CeXgx8jtsnZT3Epp3EjIw8RyHIq544="></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E="></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA="></script>
<script src="tool-example.js" defer></script> <script src="tool-example.js" defer></script>
</head> </head>
<body> <body>

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# airport dns
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# app development
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# athion turnaround
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# fawe plotsquared
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# healthcare platform
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -9,7 +9,7 @@
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU="> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=">
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E="></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# home infrastructure
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -8,7 +8,7 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
</head> </head>
<body> <body>

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# motherUoard repair
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# nitric leadership
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -9,7 +9,8 @@
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU="> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=">
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E="></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA="></script>
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# nuclear dns
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU="> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E="></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA="></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# open source success
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -24,89 +24,216 @@
<div class="project-meta"> <div class="project-meta">
<p><strong>Timeframe:</strong> 2025-Present</p> <p><strong>Timeframe:</strong> 2025-Present</p>
<p><strong>Role:</strong> Full-Stack Developer & DevOps Engineer</p> <p><strong>Role:</strong> Full-Stack Developer & DevOps Engineer</p>
<p><strong>Technologies:</strong> Python, WebDAV, WsgiDAV, macOS, ScanSnap Integration</p> <p><strong>Technologies:</strong> Python, WebDAV, WsgiDAV, macOS Integration</p>
<p><strong>Client:</strong> buildersclub.ca</p> <p><strong>Client:</strong> <a href="https://buildersclub.ca" target="_blank" rel="noopener noreferrer">buildersclub.ca</a></p>
</div> </div>
<hr> <hr>
<section class="project-overview"> <section class="project-overview">
<h2>Project Overview</h2> <h2>The Challenge</h2>
<p> <p>
The ScanSnap WebDAV Service is a high-performance document digitization solution specifically designed Running a business means dealing with receipts. Lots of them. And for buildersclub.ca members juggling multiple projects,
for buildersclub.ca members who need to rapidly process receipts and documents. The service supports managing receipt documentation was becoming a serious time sink. Traditional scanning workflows involved multiple steps:
ScanSnap scanners capable of processing up to 50 receipts at nearly 1 scan per second, providing scan, save, organize, upload. Multiply that by dozens of receipts, and you're looking at hours of manual work every week.
enterprise-grade performance for high-volume document digitization workflows.
</p> </p>
<p>
I needed a solution that could handle the club's Fujitsu ScanSnap iX1500 scanner—a beast of a machine capable of digitizing
50 receipts at nearly one scan per second—but without the usual friction of file management systems.
</p>
<div class="highlight-box"> <div class="highlight-box">
<h3>Key Performance Metrics</h3> <h3>What We Built</h3>
<p>
A custom WebDAV server optimized specifically for high-speed document scanning. Load 50 receipts, hit scan,
and watch them all digitize in under a minute. Files are immediately accessible via macOS Finder (just like
a network drive), with automatic daily cleanup to prevent storage bloat. Zero maintenance required.
</p>
<ul> <ul>
<li><strong>Batch Capacity:</strong> 50 receipts maximum per session</li>
<li><strong>Processing Speed:</strong> ~1 receipt per second</li> <li><strong>Processing Speed:</strong> ~1 receipt per second</li>
<li><strong>File Formats:</strong> PDF, JPEG, PNG (ScanSnap supported)</li> <li><strong>Batch Capacity:</strong> Up to 50 documents at once</li>
<li><strong>Storage Management:</strong> Automatic daily cleanup at 3:00 AM</li> <li><strong>File Access:</strong> Native Finder integration</li>
<li><strong>Network Protocol:</strong> WebDAV 1.0/2.0 compatible</li> <li><strong>Cleanup:</strong> Automated daily at 3:00 AM</li>
<li><strong>Network Protocol:</strong> WebDAV 1.0/2.0 compliant</li>
</ul> </ul>
<p class="highlight-note">
<strong>For buildersclub.ca members:</strong> <a href="http://192.168.1.119:9876" target="_blank">Access the scanner service here</a> (clubhouse network only)
</p>
</div> </div>
</section> </section>
<section class="technical-challenges"> <section class="technical-story">
<h2>Technical Challenges & Solutions</h2> <h2>The Technical Journey</h2>
<h3>macOS Finder WebDAV Compatibility</h3> <h3>When Finder Refuses to Play Nice</h3>
<p> <p>
One of the primary challenges was ensuring seamless integration with macOS Finder's WebDAV client. The first hurdle? Getting macOS Finder to actually connect to our WebDAV server. Turns out, Finder is incredibly
macOS Finder has specific requirements for WebDAV protocol responses that many servers don't meet by default. picky about WebDAV implementations. It expects very specific protocol responses that many standard WebDAV libraries
don't provide out of the box.
</p>
<p>
After digging through Finder's network traffic and WebDAV specs, I discovered it needed three specific "hotfixes"
that mimic Windows server behavior:
</p> </p>
<ul> <ul>
<li><strong>Problem:</strong> Finder refused to connect to the WebDAV server</li> <li><code>emulate_win32_lastmod</code> - Makes file timestamps work like Windows expects</li>
<li><strong>Solution:</strong> Implemented macOS-specific hotfixes including <code>emulate_win32_lastmod</code>, <li><code>unquote_path_info</code> - Handles special characters in file names properly</li>
<code>unquote_path_info</code>, and <code>win_accept_anonymous</code> settings</li> <li><code>win_accept_anonymous</code> - Allows Finder to connect without credentials</li>
<li><strong>Result:</strong> Full Finder compatibility with proper directory browsing and file operations</li> </ul>
<p>
Once these were in place, Finder connected instantly. The whole experience felt native—just Command+K to connect,
and boom, you've got a network drive ready for scanning.
</p>
<h3>Security Without the Headache</h3>
<p>
Here's the thing about receipt scanners: you want them to be fast and frictionless. Authentication dialogs kill that flow.
But you also can't just leave a wide-open file server exposed to the internet.
</p>
<p>
The solution? Custom permissions at the protocol level. The scanner can upload files and delete them when needed,
but it can't move, copy, or rename anything. More importantly, the service is completely isolated to its own directory—there's
literally no way for it to access files outside <code>~/scansnap-dav/scans</code>, even if someone tried to hack around it.
</p>
<pre><code>class ScanSnapProvider(FilesystemProvider):
def create_collection(self, path):
# No creating subdirectories
raise DAVError(403, "Creating directories not allowed")
def copy_resource(self, src_path, dest_path, depth):
# No copying files around
raise DAVError(403, "Copying not allowed")
def move_resource(self, src_path, dest_path):
# No moving or renaming
raise DAVError(403, "Moving/renaming not allowed")</code></pre>
<p>
For the clubhouse environment, this works perfectly. It's on a trusted network, accessible only to members,
and the restricted permissions mean there's no risk of accidentally messing up the file system.
</p>
<h3>The Storage Problem Nobody Thinks About</h3>
<p>
When you're scanning 50 receipts at a time, storage fills up fast. Even with PDF compression, you're looking at
several megabytes per scan session. Do that a few times a day, and suddenly you're managing gigabytes of receipt data.
</p>
<p>
The fix? Automatic cleanup. Every night at 3 AM, a Python scheduler wipes the scans directory clean. Receipts
are meant to be temporary anyway—scan them, grab what you need, move on. The cleanup runs silently in the background,
and members never have to think about storage management.
</p>
<pre><code>def cleanup_scans():
scans_dir = os.path.expanduser("~/scansnap-dav/scans")
for filename in os.listdir(scans_dir):
file_path = os.path.join(scans_dir, filename)
if os.isfile(file_path):
os.remove(file_path)
# Daily cleanup at 3:00 AM
schedule.every().day.at("03:00").do(cleanup_scans)</code></pre>
</section>
<section class="real-world-impact">
<h2>Real-World Impact</h2>
<h3>From Hours to Minutes</h3>
<p>
Before this system, processing a week's worth of receipts meant:
</p>
<ol>
<li>Scan receipts one by one (or in small batches)</li>
<li>Wait for files to save to the local machine</li>
<li>Open file manager and organize scans</li>
<li>Upload to cloud storage or accounting software</li>
<li>Clean up local copies to free up space</li>
</ol>
<p>
That's easily 20-30 minutes of manual work for a typical batch of receipts.
</p>
<p>
Now? Load the scanner hopper, hit scan, wait 60 seconds, grab the PDFs from Finder. Done. The time savings
are dramatic—what used to take half an hour now takes maybe two minutes.
</p>
<h3>The Numbers</h3>
<ul>
<li><strong>Time Reduction:</strong> 95% decrease in manual document processing</li>
<li><strong>Batch Efficiency:</strong> 50 receipts in under 60 seconds</li>
<li><strong>Storage Overhead:</strong> Zero (automated cleanup handles everything)</li>
<li><strong>User Training Required:</strong> Literally just "Command+K, enter the URL"</li>
</ul> </ul>
<h3>High-Volume File Processing</h3> <h3>Why It Works</h3>
<p> <p>
The service needed to handle rapid file uploads from ScanSnap scanners without performance degradation The beauty of this solution is its simplicity. There's no complex web interface, no database, no authentication system
or storage issues. to maintain. It's just a WebDAV endpoint that does exactly what the scanner needs and nothing more.
</p> </p>
<p>
For buildersclub.ca members, it means one less thing to think about. Receipts get scanned, files are immediately
available, and storage never becomes an issue. The system just works, quietly and reliably, in the background.
</p>
</section>
<section class="technical-details">
<h2>Under the Hood</h2>
<h3>The Tech Stack</h3>
<ul> <ul>
<li><strong>Problem:</strong> Managing storage space with high-volume scanning operations</li> <li><strong>Server Framework:</strong> WsgiDAV with Cheroot WSGI server</li>
<li><strong>Solution:</strong> Implemented automated cleanup scheduler with configurable timing</li> <li><strong>Language:</strong> Python 3.13+</li>
<li><strong>Result:</strong> Zero-maintenance storage management with daily automated cleanup</li> <li><strong>Automation:</strong> Python schedule library for cleanup</li>
<li><strong>macOS Integration:</strong> launchd for auto-start on boot</li>
<li><strong>Protocol:</strong> WebDAV with macOS-specific optimizations</li>
</ul> </ul>
<h3>Security & File Isolation</h3> <h3>Key Configuration</h3>
<p> <pre><code>config = {
Ensuring the WebDAV service could only access designated directories while preventing unauthorized "host": "0.0.0.0",
file operations. "port": 9876,
</p> "provider_mapping": {
"/": ScanSnapProvider(scans_dir)
},
"hotfixes": {
"emulate_win32_lastmod": True,
"unquote_path_info": True,
"win_accept_anonymous": True,
},
"property_manager": True,
"lock_storage": True,
}</code></pre>
<h3>Security Considerations</h3>
<ul> <ul>
<li><strong>Problem:</strong> Preventing access to system files and unauthorized operations</li> <li><strong>Network Scope:</strong> Clubhouse network only, no internet exposure</li>
<li><strong>Solution:</strong> Custom provider class with restricted permissions (read, create, delete only)</li> <li><strong>File Isolation:</strong> Cannot access anything outside the scans directory</li>
<li><strong>Result:</strong> Secure file isolation with blocked move/copy/directory creation operations</li> <li><strong>Operation Restrictions:</strong> Upload, read, and delete only—no move/copy/rename</li>
<li><strong>Authentication:</strong> None required (trusted network environment)</li>
</ul> </ul>
</section> </section>
<section class="results-impact"> <section class="lessons-learned">
<h2>Results & Impact</h2> <h2>Lessons Learned</h2>
<h3>Performance Improvements</h3> <h3>Sometimes Simple is Better</h3>
<ul> <p>
<li><strong>Processing Speed:</strong> 95% reduction in manual document processing time</li> I could have built a full web application with user accounts, file organization features, OCR processing,
<li><strong>Batch Efficiency:</strong> 50 receipts processed in under 60 seconds</li> automatic categorization, cloud sync... but none of that was actually needed. The scanner needed a place to
<li><strong>Storage Management:</strong> Zero-maintenance automated cleanup</li> dump files quickly, and users needed to grab those files easily. Mission accomplished with a fraction of the complexity.
<li><strong>User Experience:</strong> Seamless Finder integration with drag-and-drop functionality</li> </p>
</ul>
<h3>Business Value</h3> <h3>macOS WebDAV is Quirky</h3>
<ul> <p>
<li><strong>Cost Reduction:</strong> Eliminated manual document processing overhead</li> Apple's Finder WebDAV client has some very specific expectations that aren't always documented. The solution
<li><strong>Scalability:</strong> Supports high-volume scanning operations</li> involved reading through protocol specs, analyzing network traffic, and testing various server configurations.
<li><strong>Reliability:</strong> Automated cleanup prevents storage issues</li> Once you know the tricks (those three hotfix flags), it's actually rock solid.
<li><strong>Integration:</strong> Native macOS compatibility reduces training requirements</li> </p>
</ul>
<h3>Automatic Cleanup Changes Everything</h3>
<p>
The daily cleanup feature turned this from a "nice to have" into a "set it and forget it" solution. Nobody
thinks about storage, nobody worries about running out of space, and the system stays lean indefinitely.
</p>
</section> </section>
<hr> <hr>
@ -116,8 +243,9 @@
<ul> <ul>
<li><a href="../index.html">← Back to Portfolio</a></li> <li><a href="../index.html">← Back to Portfolio</a></li>
<li><a href="https://buildersclub.ca" target="_blank" rel="noopener noreferrer">buildersclub.ca</a></li> <li><a href="https://buildersclub.ca" target="_blank" rel="noopener noreferrer">buildersclub.ca</a></li>
<li><strong>For Members:</strong> <a href="http://192.168.1.119:9876" target="_blank">Scanner Service Access</a> (clubhouse network)</li>
<li><a href="https://github.com/mar10/wsgidav" target="_blank" rel="noopener noreferrer">WsgiDAV Framework</a></li> <li><a href="https://github.com/mar10/wsgidav" target="_blank" rel="noopener noreferrer">WsgiDAV Framework</a></li>
<li><a href="https://www.fujitsu.com/us/products/computing/peripheral/scanners/scansnap/" target="_blank" rel="noopener noreferrer">ScanSnap Scanners</a></li> <li><a href="https://www.fujitsu.com/us/products/computing/peripheral/scanners/scansnap/" target="_blank" rel="noopener noreferrer">Fujitsu ScanSnap Scanners</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# showerloop
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -9,7 +9,8 @@
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU="> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=">
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E="></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA="></script>
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# viperwire
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# weU design java
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -8,8 +8,9 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
<script src="../markdown-loader.js" integrity="sha256-4+erbuMKlaalnlqc0+5d+X4Bpr1CZ7W3dUCsyA15spE="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->

View File

@ -0,0 +1,10 @@
# wordpress security
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -8,7 +8,7 @@
<link rel="icon" type="image/x-icon" href="../favicon.ico"> <link rel="icon" type="image/x-icon" href="../favicon.ico">
<link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous"> <link rel="stylesheet" href="../styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=" crossorigin="anonymous">
<script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script> <script src="../theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4=" crossorigin="anonymous"></script>
<script src="../includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E=" crossorigin="anonymous"></script> <script src="../includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY="> <link rel="stylesheet" href="stories.css" integrity="sha256-O+OMb48leSKvekhMTDUK1y6+WG9x33kA0eDw00wUwkY=">
</head> </head>
<body> <body>

View File

@ -0,0 +1,10 @@
# youtuUe game dev
> Draft placeholder. Content to be written.
- Summary: TBD
- Key outcomes: TBD
- Tech stack: TBD
- Challenges: TBD
- Results: TBD

View File

@ -8,7 +8,7 @@
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU="> <link rel="stylesheet" href="styles.css" integrity="sha256-Y+6RTuKMnPfNa1TjCQCcFhxwo0G+xNy7t1MaAvn5SuU=">
<script src="theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script> <script src="theme.js" integrity="sha256-+dDNTo7WAOmn2YC875+vn9oH4UkMwlVOGlARp2uq3A4="></script>
<script src="includes.js" integrity="sha256-q9ac7XWqnIASoBRfs4I4hpSMlnxGARofcEw0cSFfn/E="></script> <script src="includes.js" integrity="sha256-0VPPSi+jVc1DuyZaSYTq+fnpIfv7ft+ZDenYE6pDPqA="></script>
</head> </head>
<body> <body>
<!-- Header Include --> <!-- Header Include -->