chore: capture homepage baseline

This commit is contained in:
Fi3w0 2026-08-11 16:26:50 +02:00
commit 66b9ea6054
30 changed files with 11988 additions and 0 deletions

10
.dockerignore Normal file
View file

@ -0,0 +1,10 @@
.git
.gitignore
.idea
.cache
dist
node_modules
npm-debug.log*
.DS_Store
.env
DEPLOY.md

11
.editorconfig Normal file
View file

@ -0,0 +1,11 @@
# editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

4
.env.example Normal file
View file

@ -0,0 +1,4 @@
DOMAIN=fiwlabs.dev
TRAEFIK_NETWORK=proxy
TRAEFIK_HTTPS_ENTRYPOINT=websecure
TRAEFIK_CERT_RESOLVER=porkbun

194
.gitattributes vendored Normal file
View file

@ -0,0 +1,194 @@
## GITATTRIBUTES FOR WEB PROJECTS
#
# These settings are for any web project.
#
# Details per file setting:
# text These files should be normalized (i.e. convert CRLF to LF).
# binary These files are binary and should be left untouched.
#
# Note that binary is a macro for -text -diff.
######################################################################
## AUTO-DETECT
## Handle line endings automatically for files detected as
## text and leave all files detected as binary untouched.
## This will handle all files NOT defined below.
* text=auto
## SOURCE CODE
*.bat text eol=crlf
*.coffee text
*.css text
*.htm text
*.html text
*.inc text
*.ini text
*.js text
*.json text
*.jsx text
*.less text
*.od text
*.onlydata text
*.php text
*.pl text
*.py text
*.rb text
*.sass text
*.scm text
*.scss text
*.sh text eol=lf
*.sql text
*.styl text
*.tag text
*.ts text
*.tsx text
*.xml text
*.xhtml text
## DOCKER
*.dockerignore text
Dockerfile text
## DOCUMENTATION
*.markdown text
*.md text
*.mdwn text
*.mdown text
*.mkd text
*.mkdn text
*.mdtxt text
*.mdtext text
*.txt text
AUTHORS text
CHANGELOG text
CHANGES text
CONTRIBUTING text
COPYING text
copyright text
*COPYRIGHT* text
INSTALL text
license text
LICENSE text
NEWS text
readme text
*README* text
TODO text
## TEMPLATES
*.dot text
*.ejs text
*.haml text
*.handlebars text
*.hbs text
*.hbt text
*.jade text
*.latte text
*.mustache text
*.njk text
*.phtml text
*.tmpl text
*.tpl text
*.twig text
## LINTERS
.babelrc text
.csslintrc text
.eslintrc text
.htmlhintrc text
.jscsrc text
.jshintrc text
.jshintignore text
.prettierrc text
.stylelintrc text
## CONFIGS
*.bowerrc text
*.cnf text
*.conf text
*.config text
.browserslistrc text
.editorconfig text
.gitattributes text
.gitconfig text
.gitignore text
.htaccess text
*.npmignore text
*.yaml text
*.yml text
browserslist text
Makefile text
makefile text
## HEROKU
Procfile text
.slugignore text
## GRAPHICS
*.ai binary
*.bmp binary
*.eps binary
*.gif binary
*.ico binary
*.jng binary
*.jp2 binary
*.jpg binary
*.jpeg binary
*.jpx binary
*.jxr binary
*.pdf binary
*.png binary
*.psb binary
*.psd binary
*.svg text
*.svgz binary
*.tif binary
*.tiff binary
*.wbmp binary
*.webp binary
## AUDIO
*.kar binary
*.m4a binary
*.mid binary
*.midi binary
*.mp3 binary
*.ogg binary
*.ra binary
## VIDEO
*.3gpp binary
*.3gp binary
*.as binary
*.asf binary
*.asx binary
*.fla binary
*.flv binary
*.m4v binary
*.mng binary
*.mov binary
*.mp4 binary
*.mpeg binary
*.mpg binary
*.ogv binary
*.swc binary
*.swf binary
*.webm binary
## ARCHIVES
*.7z binary
*.gz binary
*.jar binary
*.rar binary
*.tar binary
*.zip binary
## FONTS
*.ttf binary
*.eot binary
*.otf binary
*.woff binary
*.woff2 binary
## EXECUTABLES
*.exe binary
*.pyc binary

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
node_modules
dist
.cache
.idea/
*.iml
.DS_Store
.env

28
404.html Normal file
View file

@ -0,0 +1,28 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>404 - Fi3w0</title>
<meta name="robots" content="noindex">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Archivo+Black&amp;family=IBM+Plex+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css?v=4">
<link rel="stylesheet" href="/css/editorial.css?v=4">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<meta name="theme-color" content="#101a21">
</head>
<body class="error-page">
<div class="page-grid" aria-hidden="true"></div>
<main class="error-shell">
<p class="error-code">HTTP 404 / route not found</p>
<h1>Wrong path. Good terminal.</h1>
<p>The requested route is not running on this host. Return to the system index and try another direction.</p>
<a class="button button-primary" href="/">Back to fi3w0 <span aria-hidden="true">&rarr;</span></a>
</main>
</body>
</html>

50
DEPLOY.md Normal file
View file

@ -0,0 +1,50 @@
# Server deployment
The production image builds the Webpack site with Node and serves the generated `dist/` directory with Nginx. The container does not publish a host port; Traefik reaches it over an external Docker network.
## Defaults
| Setting | Default |
| --- | --- |
| Domain | `fiwlabs.dev` |
| Traefik network | `proxy` |
| HTTPS entrypoint | `websecure` |
| Certificate resolver | `porkbun` |
Compose reads overrides from `.env`. The available keys and defaults are documented in `.env.example`.
## Prerequisites
1. Point the `A` and optional `AAAA` records for `fiwlabs.dev` to the server.
2. Ensure Traefik has its Docker provider enabled.
3. Attach Traefik to the same external network configured by `TRAEFIK_NETWORK`.
4. Ensure the configured certificate resolver exists in Traefik.
Create the default shared network once if the Traefik stack has not already created it:
```sh
docker network create proxy
```
## Deploy
Validate the resolved Compose configuration:
```sh
docker compose -f docker-compose.yml config
```
Build and start the site:
```sh
docker compose -f docker-compose.yml up --build -d
```
Check container health and logs:
```sh
docker compose -f docker-compose.yml ps
docker compose -f docker-compose.yml logs -f fiws-page
```
The container exposes `/healthz` internally for Docker health checks. Traefik serves the site at `https://fiwlabs.dev`; the server's `web` entrypoint redirects HTTP requests to HTTPS globally.

21
Dockerfile Normal file
View file

@ -0,0 +1,21 @@
# syntax=docker/dockerfile:1.7
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
COPY . .
RUN npm run build
FROM nginx:1.27-alpine AS runtime
COPY deploy/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -q -O /dev/null http://127.0.0.1/healthz || exit 1

19
LICENSE.txt Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) HTML5 Boilerplate
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1816
css/editorial.css Normal file

File diff suppressed because it is too large Load diff

2747
css/style.css Normal file

File diff suppressed because it is too large Load diff

47
deploy/nginx.conf Normal file
View file

@ -0,0 +1,47 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
index index.html;
charset utf-8;
server_tokens off;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'; script-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self'" always;
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/javascript application/json application/manifest+json image/svg+xml;
location = /healthz {
access_log off;
default_type text/plain;
return 200 "ok\n";
}
location = /index.html {
expires -1;
try_files $uri =404;
}
location ~* \.(?:css|js|png|jpe?g|gif|svg|ico|webmanifest)$ {
expires 1h;
try_files $uri =404;
}
location / {
try_files $uri $uri/ =404;
}
error_page 404 /404.html;
location = /404.html {
internal;
}
}

39
docker-compose.yml Normal file
View file

@ -0,0 +1,39 @@
name: fiws-page
services:
fiws-page:
image: fiws-page:latest
container_name: fiws-page
build:
context: .
dockerfile: Dockerfile
target: runtime
restart: unless-stopped
init: true
read_only: true
security_opt:
- no-new-privileges:true
tmpfs:
- /var/cache/nginx
- /var/run
- /tmp
expose:
- "80"
networks:
- proxy
labels:
traefik.enable: "true"
traefik.docker.network: "${TRAEFIK_NETWORK:-proxy}"
traefik.http.services.fiws-page.loadbalancer.server.port: "80"
traefik.http.routers.fiws-page.rule: "Host(`${DOMAIN:-fiwlabs.dev}`)"
traefik.http.routers.fiws-page.entrypoints: "${TRAEFIK_HTTPS_ENTRYPOINT:-websecure}"
traefik.http.routers.fiws-page.service: "fiws-page"
traefik.http.routers.fiws-page.tls: "true"
traefik.http.routers.fiws-page.tls.certresolver: "${TRAEFIK_CERT_RESOLVER:-porkbun}"
networks:
proxy:
external: true
name: "${TRAEFIK_NETWORK:-proxy}"

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

8
icon.svg Normal file
View file

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 192 192" role="img" aria-labelledby="title">
<title id="title">Fi3w0 terminal mark</title>
<rect width="192" height="192" rx="42" fill="#0b1014"/>
<rect x="17" y="17" width="158" height="158" rx="28" fill="none" stroke="#27343c" stroke-width="4"/>
<path d="M43 57l40 39-40 39" fill="none" stroke="#f0996e" stroke-linecap="round" stroke-linejoin="round" stroke-width="15"/>
<path d="M96 135h53" fill="none" stroke="#8fd4d0" stroke-linecap="round" stroke-width="15"/>
<circle cx="146" cy="49" r="8" fill="#c8ee80"/>
</svg>

After

Width:  |  Height:  |  Size: 611 B

BIN
img/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

BIN
img/flux-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
img/flux-overview.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
img/moonlit-shell.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

BIN
img/tidewm-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

420
index.html Normal file
View file

@ -0,0 +1,420 @@
<!doctype html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fi3w0 / Alex - Systems Builder</title>
<meta name="description" content="Alex, also known as Fi3w0, builds Linux and macOS desktop tools, homelab infrastructure, server tooling, and configurable Minecraft frameworks.">
<meta property="og:title" content="Fi3w0 / Alex - Systems Builder">
<meta property="og:description" content="Linux, homelabs, server tooling, and the systems holding everything up.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://fiwlabs.dev/">
<meta property="og:image" content="https://fiwlabs.dev/img/moonlit-shell.jpg">
<meta property="og:image:alt" content="Moonlit Shell running across a custom Arch Linux desktop">
<meta name="twitter:card" content="summary_large_image">
<link rel="canonical" href="https://fiwlabs.dev/">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Archivo+Black&amp;family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;1,400&amp;display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/style.css?v=4">
<link rel="stylesheet" href="css/editorial.css?v=4">
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="icon.png">
<link rel="manifest" href="site.webmanifest">
<meta name="theme-color" content="#101a21">
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<div class="page-grid" aria-hidden="true"></div>
<header class="site-header">
<a class="brand" href="#top" aria-label="Fi3w0 home">
<span class="brand-mark" aria-hidden="true">&gt;_</span>
<span>fi3w0</span>
</a>
<nav class="site-nav" aria-label="Primary navigation">
<span class="nav-gooey" aria-hidden="true">
<span class="nav-gooey-blob nav-gooey-blob--trail"></span>
<span class="nav-gooey-blob nav-gooey-blob--lead"></span>
</span>
<a href="#about">About</a>
<a href="#work">Work</a>
<a href="#stack">Stack</a>
<a href="#contact">Contact</a>
<a class="nav-pill" href="https://github.com/Fi3w0" target="_blank" rel="noreferrer">
GitHub <span aria-hidden="true">&nearr;</span>
</a>
</nav>
</header>
<main id="main">
<section class="hero" id="top" aria-labelledby="hero-title">
<div class="hero-liquid" id="hero-liquid" aria-hidden="true">
<pre class="hero-liquid-pre"></pre>
<p class="hero-liquid-hint">click and drag to push the water</p>
</div>
<div class="hero-copy reveal is-visible">
<p class="eyebrow"><span class="status-dot" aria-hidden="true"></span> alex@fiw:~ $ cat about.txt</p>
<h1 id="hero-title">Alex <span id="glitch-name" data-text="/ Fi3w0">/ Fi3w0</span></h1>
<p class="hero-intro">
I make Linux and macOS desktop tools, run a homelab, and publish Minecraft server mods.
Most of my projects start as something I wanted for my own machine or server,
then grow until they are useful to somebody else too.
</p>
<div class="hero-actions">
<a class="button button-primary" href="#work">What I am building <span aria-hidden="true">&darr;</span></a>
<a class="button button-ghost" href="https://modrinth.com/user/Fi3w0" target="_blank" rel="noreferrer">My Modrinth mods <span aria-hidden="true">&nearr;</span></a>
</div>
<ul class="hero-signals" aria-label="Profile details">
<li><span>01</span> Arch at the desk</li>
<li><span>02</span> Ubuntu on the server</li>
<li><span>03</span> break it, learn, repeat</li>
</ul>
</div>
<aside class="terminal-card reveal is-visible" aria-label="A snapshot of Alex's current work">
<div class="terminal-chrome">
<span>~/workbench</span>
<span class="terminal-state">LOCAL</span>
</div>
<div class="terminal-body">
<p><span class="prompt">$</span> git status --short</p>
<p class="terminal-output"><span>M</span> homelab/<br><span>M</span> minecraft-mods/<br><span>??</span> desktop-experiments/</p>
<p><span class="prompt">$</span> cat NOTES</p>
<p class="terminal-output">linux + macos / servers / modding<br>automation / config / self-hosting</p>
<p class="terminal-note"># there is always something unfinished</p>
</div>
<div class="profile-chip">
<img src="img/avatar.png" alt="Fi3w0's GitHub avatar" width="460" height="460">
<div><strong>Alex / Fi3w0</strong><span>Linux, servers, mods</span></div>
</div>
</aside>
</section>
<div class="signal-strip" aria-label="Core interests">
<div class="signal-track">
<div class="signal-set">
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/archlinux/11171a" alt="" loading="lazy" onerror="this.remove()">Arch Linux</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/ubuntu/11171a" alt="" loading="lazy" onerror="this.remove()">Ubuntu Server</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/docker/11171a" alt="" loading="lazy" onerror="this.remove()">Docker</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/modrinth/11171a" alt="" loading="lazy" onerror="this.remove()">Minecraft tooling</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/wayland/11171a" alt="" loading="lazy" onerror="this.remove()">Wayland</span><i></i>
<span class="signal-item"><img class="signal-icon" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%2311171a%22%3E%3Cpath%20d%3D%22M12%202C7.03%202%203%203.34%203%205s4.03%203%209%203%209-1.34%209-3-4.03-3-9-3z%22%2F%3E%3Cpath%20d%3D%22M3%208.2v3.3c0%201.66%204.03%203%209%203s9-1.34%209-3V8.2c-1.6%201.28-5.1%202.3-9%202.3s-7.4-1.02-9-2.3z%22%2F%3E%3Cpath%20d%3D%22M3%2013.7V17c0%201.66%204.03%203%209%203s9-1.34%209-3v-3.3c-1.6%201.28-5.1%202.3-9%202.3s-7.4-1.02-9-2.3z%22%2F%3E%3C%2Fsvg%3E" alt="">Self-hosting</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/json/11171a" alt="" loading="lazy" onerror="this.remove()">Things configured with JSON</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/gnubash/11171a" alt="" loading="lazy" onerror="this.remove()">Bash</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/rust/11171a" alt="" loading="lazy" onerror="this.remove()">Rust</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/go/11171a" alt="" loading="lazy" onerror="this.remove()">Go</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/lua/11171a" alt="" loading="lazy" onerror="this.remove()">Lua</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/kotlin/11171a" alt="" loading="lazy" onerror="this.remove()">Kotlin</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/typescript/11171a" alt="" loading="lazy" onerror="this.remove()">TypeScript</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/githubactions/11171a" alt="" loading="lazy" onerror="this.remove()">GitHub Actions</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/ffmpeg/11171a" alt="" loading="lazy" onerror="this.remove()">FFmpeg</span><i></i>
<span class="signal-item"><img class="signal-icon" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%229.6%22%20fill%3D%22none%22%20stroke%3D%22%2311171a%22%20stroke-width%3D%221.4%22%2F%3E%3Cpath%20d%3D%22M12%2C2.4%20A9.6%2C9.6%200%200%2C1%2012%2C21.6%20Z%22%20fill%3D%22%2311171a%22%2F%3E%3Ccircle%20cx%3D%228%22%20cy%3D%229.6%22%20r%3D%221.15%22%20fill%3D%22%2311171a%22%2F%3E%3Cpath%20d%3D%22M6.4%2013.8%20Q8.3%2016.6%2010.6%2014.5%22%20fill%3D%22none%22%20stroke%3D%22%2311171a%22%20stroke-width%3D%221.4%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E" alt="">macOS</span><i></i>
</div>
<div class="signal-set" aria-hidden="true">
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/archlinux/11171a" alt="" loading="lazy" onerror="this.remove()">Arch Linux</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/ubuntu/11171a" alt="" loading="lazy" onerror="this.remove()">Ubuntu Server</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/docker/11171a" alt="" loading="lazy" onerror="this.remove()">Docker</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/modrinth/11171a" alt="" loading="lazy" onerror="this.remove()">Minecraft tooling</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/wayland/11171a" alt="" loading="lazy" onerror="this.remove()">Wayland</span><i></i>
<span class="signal-item"><img class="signal-icon" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%2311171a%22%3E%3Cpath%20d%3D%22M12%202C7.03%202%203%203.34%203%205s4.03%203%209%203%209-1.34%209-3-4.03-3-9-3z%22%2F%3E%3Cpath%20d%3D%22M3%208.2v3.3c0%201.66%204.03%203%209%203s9-1.34%209-3V8.2c-1.6%201.28-5.1%202.3-9%202.3s-7.4-1.02-9-2.3z%22%2F%3E%3Cpath%20d%3D%22M3%2013.7V17c0%201.66%204.03%203%209%203s9-1.34%209-3v-3.3c-1.6%201.28-5.1%202.3-9%202.3s-7.4-1.02-9-2.3z%22%2F%3E%3C%2Fsvg%3E" alt="">Self-hosting</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/json/11171a" alt="" loading="lazy" onerror="this.remove()">Things configured with JSON</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/gnubash/11171a" alt="" loading="lazy" onerror="this.remove()">Bash</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/rust/11171a" alt="" loading="lazy" onerror="this.remove()">Rust</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/go/11171a" alt="" loading="lazy" onerror="this.remove()">Go</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/lua/11171a" alt="" loading="lazy" onerror="this.remove()">Lua</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/kotlin/11171a" alt="" loading="lazy" onerror="this.remove()">Kotlin</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/typescript/11171a" alt="" loading="lazy" onerror="this.remove()">TypeScript</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/githubactions/11171a" alt="" loading="lazy" onerror="this.remove()">GitHub Actions</span><i></i>
<span class="signal-item"><img class="signal-icon" src="https://cdn.simpleicons.org/ffmpeg/11171a" alt="" loading="lazy" onerror="this.remove()">FFmpeg</span><i></i>
<span class="signal-item"><img class="signal-icon" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%229.6%22%20fill%3D%22none%22%20stroke%3D%22%2311171a%22%20stroke-width%3D%221.4%22%2F%3E%3Cpath%20d%3D%22M12%2C2.4%20A9.6%2C9.6%200%200%2C1%2012%2C21.6%20Z%22%20fill%3D%22%2311171a%22%2F%3E%3Ccircle%20cx%3D%228%22%20cy%3D%229.6%22%20r%3D%221.15%22%20fill%3D%22%2311171a%22%2F%3E%3Cpath%20d%3D%22M6.4%2013.8%20Q8.3%2016.6%2010.6%2014.5%22%20fill%3D%22none%22%20stroke%3D%22%2311171a%22%20stroke-width%3D%221.4%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E" alt="">macOS</span><i></i>
</div>
</div>
</div>
<section class="section about-section" id="about" aria-labelledby="about-title">
<header class="section-heading reveal">
<p class="section-index">01 / About</p>
<h2 id="about-title">What I actually do.</h2>
</header>
<div class="about-layout">
<blockquote class="about-quote reveal">
<span class="quote-mark" aria-hidden="true">&ldquo;</span>
<p>I care more about the whole machine than winning an argument about one language.</p>
<footer>Design it, configure it, deploy it, then fix the boring parts.</footer>
</blockquote>
<div class="about-copy reveal">
<p class="lead">
Some weeks I am building a Minecraft framework. Other weeks I replace half my desktop
shell or add something unnecessary to the mini PC under my desk.
</p>
<p>
My projects jump between low-level desktop work and data-driven server tools, but the part I enjoy
is the same: turning rough pieces into something I can run and maintain myself.
</p>
<p>
Linux, systemd, Docker, Traefik, backups, and CI pipelines are familiar territory. I like
hot reload, readable config files, updates that can fail safely, and logs that say what went wrong.
</p>
<p>
I write Bash, Go, Lua, and modify Java directly. For Rust, Swift, and larger Kotlin, TypeScript, Python,
and QML builds, I use AI agents heavily, then review, test, and integrate the result myself.
</p>
<div class="language-line">
<span>Ukrainian + Russian</span><span>Spanish</span><span>English C1</span>
</div>
</div>
</div>
<div class="method-grid">
<article class="method-card reveal">
<span>01</span><h3>Start with the mess</h3><p>Work out the data, failure cases, and who has to maintain it before choosing the clever part.</p>
</article>
<article class="method-card reveal">
<span>02</span><h3>Automate repeats</h3><p>If I have to do it twice, it probably needs a script, an installer, or a CI job.</p>
</article>
<article class="method-card reveal">
<span>03</span><h3>Make it recoverable</h3><p>Reboots, bad input, old versions, and my own mistakes are normal operating conditions.</p>
</article>
</div>
</section>
<section class="section work-section" id="work" aria-labelledby="work-title">
<header class="section-heading section-heading-row reveal">
<div><p class="section-index">02 / Work</p><h2 id="work-title">Projects, tools, experiments.</h2></div>
<p>Public repositories, installable mods, and rougher ideas that started on my own machines.</p>
</header>
<article class="current-project reveal" aria-labelledby="tidewm-title">
<div class="current-project-main">
<div class="current-meta"><span><i aria-hidden="true"></i> Daily-driver compositor</span><span>AI-assisted / evolving</span></div>
<p class="current-kicker">Wayland compositor / Rust</p>
<div class="tide-heading">
<img class="tide-mark" src="img/tidewm-logo.png" alt="" width="48" height="48" loading="lazy">
<h3 id="tidewm-title">TideWM</h3>
</div>
<p>A water-themed Wayland compositor built on Smithay, made because I wanted to see how far I could take it. Tiling (BSP and master/stack), multi-monitor, XWayland, workspaces, and IPC already run as my daily driver; the signature aqua render effects are next.</p>
<div class="tag-row current-tags"><span>AI-assisted</span><span>Smithay</span><span>Rust</span><span>Wayland</span></div>
<a class="text-link current-repo-link" href="https://github.com/Fi3w0/TideWM" target="_blank" rel="noreferrer">View repository <span aria-hidden="true">&nearr;</span></a>
</div>
<div class="current-roadmap">
<p>Project shape</p>
<ol>
<li><span>01</span><div><strong>Foundation</strong><small>Rust compositor built directly on Smithay</small></div><b>base</b></li>
<li><span>02</span><div><strong>Tiling &amp; compat</strong><small>BSP/master-stack layouts, multi-monitor, XWayland</small></div><b>done</b></li>
<li><span>03</span><div><strong>Visual direction</strong><small>Water/aqua render effects, still pending</small></div><b>next</b></li>
<li><span>04</span><div><strong>Development method</strong><small>AI-assisted implementation, tested and reviewed by me</small></div><b>method</b></li>
</ol>
</div>
<span class="current-watermark" aria-hidden="true">TIDE</span>
<div class="tide-ascii" id="tide-ascii" aria-hidden="true"></div>
</article>
<article class="project-feature reveal" data-repo="Flux">
<a class="project-visual project-visual-flux" href="https://github.com/Fi3w0/Flux" target="_blank" rel="noreferrer" aria-label="View Flux on GitHub">
<span class="flux-chrome" aria-hidden="true"><i></i><i></i><i></i></span>
<img class="flux-icon" src="img/flux-icon.png" alt="" width="256" height="256" loading="lazy">
<span class="flux-tags" aria-hidden="true"><span>SwiftUI</span><span>AppKit</span><span>Local-first</span></span>
<span class="flux-stack" aria-hidden="true">
<b>Tool stack</b>
<span class="flux-stack-row"><span>01</span><span>FFmpeg<small>transcode + inspect</small></span></span>
<span class="flux-stack-row"><span>02</span><span>yt-dlp<small>downloads</small></span></span>
<span class="flux-stack-row"><span>03</span><span>Apple Vision<small>subject isolation</small></span></span>
<span class="flux-stack-row"><span>04</span><span>Sparkle<small>auto updates</small></span></span>
</span>
<span class="flux-status" aria-hidden="true"><i></i> pre-flight checks passed</span>
<span class="flux-watermark" aria-hidden="true">FLUX</span>
<div class="flux-ascii" id="flux-ascii" aria-hidden="true"></div>
<span class="image-label">macOS Tahoe / Apple silicon</span>
</a>
<div class="project-feature-copy">
<div class="project-meta"><span>Latest project</span><span>SwiftUI / AppKit / macOS</span></div>
<h3>Flux</h3>
<p>A native macOS media workbench: convert, download, edit, and compose media in one focused app. FFmpeg, yt-dlp, and Apple Vision underneath, pre-flight checks that explain failures before a job ever starts, and everything processed locally.</p>
<ul class="project-facts">
<li><strong>7</strong><span>media engines</span></li>
<li><strong>0</strong><span>telemetry</span></li>
<li><strong>100%</strong><span>local processing</span></li>
</ul>
<a class="text-link" href="https://github.com/Fi3w0/Flux" target="_blank" rel="noreferrer">View repository <span aria-hidden="true">&nearr;</span></a>
</div>
</article>
<article class="project-feature reveal" data-repo="Moonlit-shell">
<a class="project-visual" href="https://github.com/Fi3w0/Moonlit-shell" target="_blank" rel="noreferrer" aria-label="View Moonlit Shell on GitHub">
<img src="img/moonlit-shell.jpg" alt="Moonlit Shell desktop with its top bar over a nocturnal landscape" width="1600" height="900">
<span class="image-label">Daily driver / ThinkPad T14</span>
</a>
<div class="project-feature-copy">
<div class="project-meta"><span>Featured system</span><span>QML / Go / Linux</span></div>
<h3>Moonlit Shell</h3>
<p>This is the desktop I actually use: Arch, Hyprland, and Quickshell with twelve panels, a live settings app, safe dotfile updates, and no monitoring daemon between the UI and <code>/proc</code>.</p>
<ul class="project-facts">
<li><strong>12</strong><span>native panels</span></li>
<li><strong>1</strong><span>live config</span></li>
<li><strong>0</strong><span>glue daemons</span></li>
</ul>
<a class="text-link" href="https://github.com/Fi3w0/Moonlit-shell" target="_blank" rel="noreferrer">View repository <span aria-hidden="true">&nearr;</span></a>
</div>
</article>
<div class="project-grid">
<article class="project-card project-card-warm reveal" data-repo="Fiw-Bosses">
<div class="project-card-top"><span class="project-number">01</span><span class="repo-live"><i></i> published</span></div>
<div>
<p class="project-kicker">Framework / Java</p>
<h3>FIW Bosses</h3>
<p>A JSON-driven, multi-loader boss framework with HP phases, custom minions, dialogue, loot, hot reload, and more than 50 configurable abilities.</p>
</div>
<div class="card-footer">
<div class="tag-row"><span>Fabric</span><span>NeoForge</span><span>Forge</span></div>
<a href="https://github.com/Fi3w0/Fiw-Bosses" target="_blank" rel="noreferrer" aria-label="View FIW Bosses on GitHub">&nearr;</a>
</div>
</article>
<article class="project-card project-card-cool reveal" data-repo="Fiw-Tools">
<div class="project-card-top"><span class="project-number">02</span><span class="repo-live"><i></i> published</span></div>
<div>
<p class="project-kicker">Framework / Kotlin</p>
<h3>FIW Custom Items</h3>
<p>Server-side custom weapons, armor, crafting, curses, set bonuses, charges, and abilities built entirely through JSON. Vanilla clients can connect.</p>
</div>
<div class="card-footer">
<div class="tag-row"><span>JSON</span><span>Hot reload</span><span>Server-side</span></div>
<a href="https://github.com/Fi3w0/Fiw-Tools" target="_blank" rel="noreferrer" aria-label="View FIW Custom Items on GitHub">&nearr;</a>
</div>
</article>
</div>
<div class="project-list">
<article class="project-row reveal" data-repo="Fiw-Anticheat">
<span class="row-index">03</span>
<div><p>Security tooling</p><h3>FIW AntiCheat</h3></div>
<p>Nonce challenges, mod signatures, resource-pack auditing, and honest limits across six loader/version targets.</p>
<span class="row-tech">Java / SHA-256</span>
<a href="https://github.com/Fi3w0/Fiw-Anticheat" target="_blank" rel="noreferrer" aria-label="View FIW AntiCheat on GitHub">&nearr;</a>
</article>
<article class="project-row reveal" data-repo="Fiw-Graves">
<span class="row-index">04</span>
<div><p>Reliability</p><h3>FIW Graves</h3></div>
<p>Packet-only grave markers, world-native persistence, rollback history, and a second loot safety net.</p>
<span class="row-tech">Java / Packets</span>
<a href="https://github.com/Fi3w0/Fiw-Graves" target="_blank" rel="noreferrer" aria-label="View FIW Graves on GitHub">&nearr;</a>
</article>
<article class="project-row reveal">
<span class="row-index">05</span>
<div><p>Server operations</p><h3>FIW Admin Utilities</h3></div>
<p>Maintenance mode, lag watchdog, entity sweeping, vanish, freeze, and item bans in one lightweight admin mod.</p>
<span class="row-tech">Fabric / NeoForge</span>
<a href="https://modrinth.com/mod/fiw-admin-utilitys" target="_blank" rel="noreferrer" aria-label="View FIW Admin Utilities on Modrinth">&nearr;</a>
</article>
</div>
<div class="modrinth-panel reveal">
<header class="modrinth-heading">
<div>
<p class="modrinth-label"><span aria-hidden="true"></span> Published on Modrinth</p>
<h3>Mods people can install.</h3>
</div>
<a class="text-link" href="https://modrinth.com/user/Fi3w0" target="_blank" rel="noreferrer">View creator profile <span aria-hidden="true">&nearr;</span></a>
</header>
<p class="modrinth-summary">Every mod above (plus Warp, Graves, and Tickwatch from the archive) ships through one Modrinth profile — same JSON-config philosophy, same hot reload, across Fabric, Forge, and NeoForge.</p>
<div class="modrinth-stats">
<div><strong>7</strong><span>mods published</span></div>
<div><strong>3</strong><span>loaders supported</span></div>
<div><strong>1</strong><span>creator profile</span></div>
</div>
</div>
</section>
<section class="section archive-section" aria-labelledby="archive-title">
<header class="section-heading section-heading-row reveal">
<div><p class="section-index">03 / Repositories</p><h2 id="archive-title">Other things I made.</h2></div>
<a class="text-link" href="https://github.com/Fi3w0?tab=repositories" target="_blank" rel="noreferrer">All repositories <span aria-hidden="true">&nearr;</span></a>
</header>
<div class="archive-grid">
<a class="archive-item reveal" href="https://github.com/Fi3w0/Fiw-Warp" target="_blank" rel="noreferrer"><span>Fiw Warp</span><small>Kotlin / server teleports</small><b>&nearr;</b></a>
<a class="archive-item reveal" href="https://github.com/Fi3w0/Fiw-Clock" target="_blank" rel="noreferrer"><span>Tickwatch</span><small>Crash-safe player stats</small><b>&nearr;</b></a>
<a class="archive-item reveal" href="https://github.com/Fi3w0/Firefox-Privacy" target="_blank" rel="noreferrer"><span>Firefox Privacy</span><small>Cross-platform hardening</small><b>&nearr;</b></a>
<a class="archive-item reveal" href="https://github.com/Fi3w0/HomePage" target="_blank" rel="noreferrer"><span>HomePage</span><small>Live homelab dashboard</small><b>&nearr;</b></a>
<a class="archive-item reveal" href="https://github.com/Fi3w0/FiwStoryMod" target="_blank" rel="noreferrer"><span>Fiw Story Mod</span><small>SMP artifacts + lore</small><b>&nearr;</b></a>
<a class="archive-item reveal" href="https://github.com/Fi3w0/Naoya-Mod" target="_blank" rel="noreferrer"><span>Naoya Mod</span><small>Fabric ability system</small><b>&nearr;</b></a>
<a class="archive-item reveal" href="https://github.com/Fi3w0/DevourerPlugin" target="_blank" rel="noreferrer"><span>Devourer Plugin</span><small>Asymmetric Paper PvP</small><b>&nearr;</b></a>
<a class="archive-item reveal" href="https://github.com/Fi3w0/Fi3w0-Hyprland" target="_blank" rel="noreferrer"><span>Fi3w0 Hyprland</span><small>The original daily dots</small><b>&nearr;</b></a>
<a class="archive-item reveal" href="https://github.com/Fi3w0/Fiw-ModChecklist" target="_blank" rel="noreferrer"><span>Fiw ModChecklist</span><small>Whitelist prototype</small><b>&nearr;</b></a>
<a class="archive-item reveal" href="https://github.com/Fi3w0/VeeBotV4.2" target="_blank" rel="noreferrer"><span>VeeBot V4.2</span><small>Python / Discord</small><b>&nearr;</b></a>
<a class="archive-item reveal archive-item-paused" href="https://github.com/Fi3w0/Brota" target="_blank" rel="noreferrer"><span>Brota</span><small>Paused mobile prototype</small><b>&nearr;</b></a>
</div>
</section>
<section class="section stack-section" id="stack" aria-labelledby="stack-title">
<header class="section-heading reveal">
<p class="section-index">04 / Tools</p>
<h2 id="stack-title">What is usually open in my terminal.</h2>
<p class="stack-intro">Kitty as the daily emulator, running on a MacBook (macOS) — my main device — SSH'd out to the Arch desktop and the Ubuntu server for everything else.</p>
</header>
<div class="stack-layout">
<article class="stack-card stack-card-main reveal">
<p class="stack-label">Home turf</p>
<h3>Linux / systems</h3>
<div class="stack-tags"><span class="tag-strong">Arch Linux</span><span class="tag-strong">Bash</span><span>Ubuntu Server</span><span>systemd</span><span>SSH</span><span>Networking</span></div>
</article>
<article class="stack-card reveal">
<p class="stack-label">Ship + operate</p>
<h3>Infrastructure</h3>
<div class="stack-tags"><span class="tag-strong">Docker</span><span class="tag-strong">Compose</span><span>Traefik</span><span>Portainer</span><span>GitHub Actions</span><span>Backups</span></div>
</article>
<article class="stack-card reveal">
<p class="stack-label">Build surface</p>
<h3>Code + config</h3>
<div class="stack-tags"><span class="tag-strong">Go</span><span class="tag-strong">Lua</span><span>Rust / AI-assisted</span><span>Java</span><span>Kotlin</span><span>TypeScript</span><span>QML</span><span>JSON</span></div>
</article>
<article class="stack-card stack-card-learning reveal">
<p class="stack-label">Learning next</p>
<h3>Kubernetes, Terraform, Ansible</h3>
<p>I am learning these when a real problem gives me a reason, not just to collect more logos.</p>
</article>
</div>
</section>
<section class="contact-section" id="contact" aria-labelledby="contact-title">
<div class="contact-orbit" aria-hidden="true"><span></span><span></span><span></span></div>
<p class="section-index reveal">05 / Contact</p>
<h2 id="contact-title" class="reveal">Say hello.</h2>
<p class="contact-copy reveal">Linux, infrastructure, self-hosting, Minecraft tooling, or a strange project you cannot stop thinking about are all good reasons to message me.</p>
<div class="contact-links reveal">
<a href="https://github.com/Fi3w0" target="_blank" rel="noreferrer"><span>GitHub</span><small>@Fi3w0</small><b>&nearr;</b></a>
<a href="https://t.me/fi3w0" target="_blank" rel="noreferrer"><span>Telegram</span><small>@fi3w0</small><b>&nearr;</b></a>
<button type="button" id="copy-discord"><span>Discord</span><small id="discord-label">fi3w0 - click to copy</small><b aria-hidden="true">+</b></button>
</div>
<p class="copy-status" id="copy-status" role="status" aria-live="polite"></p>
</section>
</main>
<footer class="site-footer">
<a class="brand" href="#top"><span class="brand-mark" aria-hidden="true">&gt;_</span><span>fi3w0</span></a>
<p><a href="https://git.fiwlabs.dev/" target="_blank" rel="noreferrer">git.fiwlabs.dev</a> / self-hosted Git</p>
<p class="footer-prompt" aria-hidden="true"><span class="prompt">$</span> exit 0<span class="cursor-blink">_</span></p>
<p>&copy; <span id="year">2026</span> Alex / Fi3w0</p>
</footer>
<script src="js/app.js?v=4" defer></script>
</body>
</html>

1354
js/app.js Normal file

File diff suppressed because it is too large Load diff

5116
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

26
package.json Normal file
View file

@ -0,0 +1,26 @@
{
"name": "fi3w0-portfolio",
"version": "1.0.0",
"description": "Personal systems and development portfolio for Alex (Fi3w0).",
"private": true,
"keywords": [
"portfolio",
"devops",
"systems",
"homelab"
],
"license": "MIT",
"author": "Alex (Fi3w0)",
"scripts": {
"start": "webpack serve --open --config webpack.config.dev.js",
"build": "webpack --config webpack.config.prod.js"
},
"devDependencies": {
"copy-webpack-plugin": "^11.0.0",
"html-webpack-plugin": "^5.6.0",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4",
"webpack-merge": "^5.10.0"
}
}

5
robots.txt Normal file
View file

@ -0,0 +1,5 @@
# www.robotstxt.org/
# Allow crawling of all content
User-agent: *
Disallow:

15
site.webmanifest Normal file
View file

@ -0,0 +1,15 @@
{
"short_name": "Fi3w0",
"name": "Fi3w0 / Alex - Systems Builder",
"icons": [
{
"src": "icon.png",
"type": "image/png",
"sizes": "192x192"
}
],
"start_url": "/",
"display": "standalone",
"background_color": "#101a21",
"theme_color": "#101a21"
}

12
webpack.common.js Normal file
View file

@ -0,0 +1,12 @@
const path = require('path');
module.exports = {
entry: {
app: './js/app.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
clean: true,
filename: './js/app.js',
},
};

13
webpack.config.dev.js Normal file
View file

@ -0,0 +1,13 @@
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
liveReload: true,
hot: true,
open: true,
static: ['./'],
},
});

26
webpack.config.prod.js Normal file
View file

@ -0,0 +1,26 @@
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = merge(common, {
mode: 'production',
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
inject: false,
}),
new CopyPlugin({
patterns: [
{ from: 'img', to: 'img' },
{ from: 'css', to: 'css' },
{ from: 'icon.svg', to: 'icon.svg' },
{ from: 'favicon.ico', to: 'favicon.ico' },
{ from: 'robots.txt', to: 'robots.txt' },
{ from: 'icon.png', to: 'icon.png' },
{ from: '404.html', to: '404.html' },
{ from: 'site.webmanifest', to: 'site.webmanifest' },
],
}),
],
});