[
  {
    "coffee": 5,
    "content": "# Building This Dashboard: A Cyberpunk Dev Log\n\nYou're looking at it. This site. The one with the scanlines, the particle system, the glitching text, the fake terminal, and the server metrics that are actually real.\n\n## Why?\n\nEvery developer has a portfolio site. Most look the same \u2014 clean, minimal, forgettable. I wanted something that felt like logging into a system. Something that made you feel like you just SSH'd into my brain.\n\n## The Rules\n\n1. **No frameworks** \u2014 Vanilla HTML, CSS, JS only\n2. **No templates** \u2014 Every line written from scratch\n3. **Real data** \u2014 Server metrics, weather, and now-playing are live\n4. **Cyberpunk aesthetic** \u2014 Dark theme, neon accents, monospace everything\n\n## The HUD Grid\n\nThe hero section is a CSS Grid layout. Left column: UK map (SVG with animated pulse rings) + identity block. Right column: Server metrics + containers + weather + now playing.\n\n## The Particle System\n\nThe background particles are drawn on a canvas element. Each frame: move particles based on velocity, check distance to mouse cursor, draw connection lines between nearby particles, recycle particles that leave the viewport. ~200 particles at 60fps. Surprisingly lightweight.\n\n## Live Data Integration\n\nThe server metrics aren't fake. They hit a Flask API running on the same VPS. Same for weather (Open-Meteo API) and the now-playing widget (rotating through my playlist).\n\n## The Glitch Effect\n\nThe title uses CSS clip-path animations with pseudo-elements offset by a few pixels in cyan and red. Triggered randomly every few seconds. Subtle enough to not be annoying.\n\n## What I'd Do Differently\n\nHonestly? Not much. This was a passion project built at 3AM with coffee and beats. It's not perfect, but it's mine. The entire site is about 1,600 lines of CSS and 800 lines of JS. No build step, no dependencies, no npm. Just files and a server.",
    "date": "2026-03-30",
    "difficulty": 3,
    "energy": 3,
    "excerpt": "The story behind jaeswift.xyz \u2014 how I built a cyberpunk HUD dashboard as a personal homepage using nothing but vanilla HTML, CSS, and JavaScript.",
    "focus": 4,
    "heart_rate": 76,
    "mood": "creative",
    "motivation": 5,
    "slug": "building-this-dashboard",
    "tags": [
      "webdev",
      "css",
      "javascript",
      "design",
      "devlog"
    ],
    "threat_level": "LOW",
    "time_written": "04:22",
    "title": "Building This Dashboard: A Cyberpunk Dev Log"
  },
  {
    "coffee": 3,
    "content": "## The Breaking Point\n\nThere comes a moment in every developer's life when you look at your Google Drive, your Gmail, your hosted repos on GitHub, and you think: *why am I handing all of this to someone else?* For me, that moment came at 2 AM on a Tuesday, staring at a Terms of Service update email that basically said 'we own your soul now.'\n\nSo I did what any rational person would do. I bought a VPS and started building.\n\n## The Stack\n\nThe foundation is a Debian box sitting in a data centre somewhere in Europe. On top of that:\n\n- **Gitea** for git hosting \u2014 lightweight, fast, and mine\n- **Plex** for media \u2014 because why pay for streaming when you have a 2TB drive\n- **Nginx** as the reverse proxy tying it all together\n- **Docker** containers for everything that can be containerised\n- **WireGuard** for secure remote access\n\nThe whole thing runs on 4GB of RAM and barely breaks a sweat.\n\n## The Hard Parts\n\nDNS was a nightmare at first. Getting wildcard SSL certs with Let's Encrypt took three attempts and a lot of swearing. Docker networking still makes me want to throw my laptop out the window sometimes.\n\nBut the worst part? Email. Self-hosted email is a rabbit hole I'm still climbing out of. Every major provider treats your IP as spam by default. You need SPF, DKIM, DMARC, a reverse DNS entry, and probably a blood sacrifice to get Gmail to accept your messages.\n\n## Was It Worth It?\n\nAbsolutely. I own my data. I control my infrastructure. When a service goes down, it's my fault and I can fix it. There's something deeply satisfying about `ssh root@mybox` and knowing that everything running on that machine is mine.\n\nThe total cost? About \u00a38 a month for the VPS. That's less than a single streaming subscription.\n\n## What's Next\n\nI'm looking at adding a self-hosted AI inference server, a personal search engine, and maybe a Matrix server for comms. The empire keeps growing.",
    "date": "2026-03-28",
    "difficulty": 3,
    "energy": 5,
    "excerpt": "How I ditched big tech and built my own infrastructure from the ground up. Gitea, Plex, search engines \u2014 all on one box.",
    "focus": 4,
    "heart_rate": 82,
    "id": 1,
    "mood": "productive",
    "motivation": 5,
    "slug": "self-hosted-empire",
    "tags": [
      "self-hosted",
      "infrastructure",
      "linux",
      "docker"
    ],
    "threat_level": "LOW",
    "time_written": "02:34 AM",
    "title": "Building a Self-Hosted Empire",
    "word_count": 347
  },
  {
    "coffee": 3,
    "content": "# Homelab Chronicles: Docker, Proxmox & 10 Containers Deep\n\nThere's something deeply satisfying about running your own infrastructure. No cloud bills. No vendor lock-in. Just you, a box of silicon, and a terminal.\n\n## The Hardware\n\nMy current setup is a single Contabo VPS \u2014 nothing fancy, but it punches well above its weight:\n\n- **CPU:** 6 vCPU AMD EPYC\n- **RAM:** 16GB DDR4\n- **Storage:** 400GB NVMe SSD\n- **OS:** Debian 12 (Bookworm)\n- **Location:** EU datacenter\n\n## The Stack\n\nEverything runs in Docker containers behind an Nginx reverse proxy with Let's Encrypt SSL. Here's what's running:\n\n### Core Infrastructure\n1. **Nginx Proxy Manager** \u2014 SSL termination, reverse proxy for everything\n2. **Portainer** \u2014 Container management UI (though I mostly use CLI)\n3. **Uptime Kuma** \u2014 Monitoring dashboard for all services\n\n### Development\n4. **Gitea** \u2014 Self-hosted Git at git.jaeswift.xyz\n5. **Drone CI** \u2014 Continuous integration pipelines\n6. **Agent Zero** \u2014 AI agent framework for automation\n\n### Applications\n7. **Yoink** \u2014 Custom SvelteKit media downloader\n8. **Vane** \u2014 Weather dashboard built with Next.js\n9. **Ghost** \u2014 Blog platform (since migrated to custom static)\n10. **Wireguard** \u2014 VPN tunnel for secure access\n\n## Docker Compose Setup\n\nThe entire stack is defined in a single `docker-compose.yml` with proper networking. Each service gets its own network segment. Only services that need external access join the proxy network.\n\n## Monitoring\n\nUptime Kuma pings every service every 60 seconds. I get Discord webhook alerts if anything drops. Current uptime across all services: **99.7%** over the last 30 days.\n\n## Lessons Learned\n\n1. **Always have backups** \u2014 I learned this the hard way when a disk migration went wrong\n2. **Document everything** \u2014 Future you will thank present you\n3. **Don't over-engineer** \u2014 Start simple, add complexity when needed\n4. **Security first** \u2014 Fail2ban, UFW, SSH keys only, no root login\n\nThe homelab never sleeps. Neither do I, apparently.",
    "date": "2026-03-28",
    "difficulty": 3,
    "energy": 4,
    "excerpt": "A deep dive into my self-hosted infrastructure \u2014 from bare metal to a fleet of Docker containers running everything from Gitea to media servers.",
    "focus": 4,
    "heart_rate": 72,
    "mood": "productive",
    "motivation": 5,
    "slug": "homelab-chronicles",
    "tags": [
      "homelab",
      "docker",
      "self-hosting",
      "infrastructure"
    ],
    "threat_level": "LOW",
    "time_written": "02:15",
    "title": "Homelab Chronicles: Docker, Proxmox & 10 Containers Deep"
  },
  {
    "coffee": 4,
    "content": "## You Just Deployed a Server. You're Already Under Attack.\n\nI'm not being dramatic. Within five minutes of spinning up a fresh VPS, check your auth logs. You'll see hundreds of brute-force SSH attempts from IPs all over the world. Bots are scanning every IP range constantly, looking for default credentials and open ports.\n\nWelcome to the internet.\n\n## Step 1: SSH Hardening\n\nFirst thing, always:\n\n```bash\n# Generate a key pair on your local machine\nssh-keygen -t ed25519 -C \"your@email.com\"\n\n# Copy it to the server\nssh-copy-id root@your-server\n\n# Then disable password auth\nsed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config\nsystemctl restart sshd\n```\n\nChange the default SSH port while you're at it. Security through obscurity isn't real security, but it cuts the noise in your logs by 95%.\n\n## Step 2: Firewall\n\nUFW is your friend:\n\n```bash\nufw default deny incoming\nufw default allow outgoing\nufw allow 2222/tcp  # your SSH port\nufw allow 80/tcp\nufw allow 443/tcp\nufw enable\n```\n\nThat's it. Everything else gets dropped.\n\n## Step 3: Fail2Ban\n\nFail2ban watches your logs and auto-bans IPs that fail authentication too many times. Install it, configure it for SSH, and forget about it. It'll do its job quietly in the background.\n\n## Step 4: Unattended Upgrades\n\nSecurity patches should install themselves. You shouldn't need to remember to run `apt update` every day:\n\n```bash\napt install unattended-upgrades\ndpkg-reconfigure unattended-upgrades\n```\n\n## Step 5: The Paranoia Layer\n\n- Disable root login over SSH (use a regular user + sudo)\n- Set up 2FA with Google Authenticator PAM module\n- Monitor with Netdata or similar\n- Regular `lynis audit system` scans\n- Keep backups. Always keep backups.\n\n## The Mindset\n\nSecurity isn't a destination, it's a process. You're never 'done' securing a server. New vulnerabilities drop daily. The trick is making your box harder to crack than the next one. Attackers are lazy \u2014 they'll move on to easier targets.",
    "date": "2026-03-25",
    "difficulty": 4,
    "energy": 4,
    "excerpt": "Your fresh VPS is a sitting duck. Here's how I lock mine down \u2014 SSH keys, fail2ban, firewalls, and a healthy dose of paranoia.",
    "focus": 5,
    "heart_rate": 91,
    "id": 2,
    "mood": "focused",
    "motivation": 5,
    "slug": "vps-hardening-101",
    "tags": [
      "security",
      "linux",
      "sysadmin",
      "hardening"
    ],
    "threat_level": "HIGH",
    "time_written": "11:47 PM",
    "title": "Securing the Perimeter: VPS Hardening 101",
    "word_count": 312
  },
  {
    "coffee": 5,
    "content": "# CTF Write-Up: Breaking Into the Matrix\n\n**Platform:** HackTheBox\n**Difficulty:** Medium\n**Time:** 4 hours 23 minutes\n**Tools:** nmap, burpsuite, john, linpeas\n\n---\n\n## Reconnaissance\n\nStarted with the usual nmap scan. Ports open: 22 (OpenSSH 8.9), 80 (Nginx redirects to matrix.htb), 8080 (Node.js API).\n\n## Web Enumeration\n\nThe main site was a futuristic dashboard (ironic, given my own site). Gobuster revealed /api/v1/users, /api/v1/auth, /api/v1/admin (403), and /internal/health (SSRF candidate).\n\n## The SSRF\n\nThe /api/v1/users endpoint had a profile picture feature that fetched URLs server-side. Classic SSRF. Fetched the internal health endpoint which leaked server config including a JWT secret.\n\n## JWT Manipulation\n\nWith the secret, I forged an admin token using PyJWT. Used this to access /api/v1/admin \u2014 which had a command execution endpoint.\n\n## User Shell\n\nUsed the command exec endpoint to fire a reverse shell. Got in as www-data. User flag captured.\n\n## Privilege Escalation\n\nRan linpeas. Found a SUID binary in /opt/matrix-monitor owned by root. Strings revealed it called curl without a full path. Path hijack \u2014 created a fake curl that spawns bash -p. Root shell. GG.\n\n## Takeaways\n\n- SSRF is still everywhere \u2014 always test URL input fields\n- JWT secrets in internal endpoints = instant admin\n- SUID binaries with relative paths are free root\n- Always run linpeas. Always.",
    "date": "2026-03-25",
    "difficulty": 4,
    "energy": 5,
    "excerpt": "A detailed write-up of a medium-difficulty HackTheBox challenge involving SSRF, JWT manipulation, and a satisfying root shell.",
    "focus": 5,
    "heart_rate": 88,
    "mood": "focused",
    "motivation": 5,
    "slug": "ctf-matrix-writeup",
    "tags": [
      "ctf",
      "hackthebox",
      "pentesting",
      "write-up",
      "security"
    ],
    "threat_level": "HIGH",
    "time_written": "23:48",
    "title": "CTF Write-Up: Breaking Into the Matrix (HackTheBox)"
  },
  {
    "coffee": 2,
    "content": "# OpSec for the Paranoid: My Privacy Stack\n\nI'm not hiding anything. I just don't think anyone's entitled to my data.\n\n## DNS\n\nSelf-hosted Pi-hole pointing to Quad9 (9.9.9.9) with DNSSEC enabled. Every DNS query on my network is filtered and encrypted. The blocklist catches ~30% of all queries.\n\n## Email\n\nProtonMail for personal. Self-hosted Postfix/Dovecot on jaeswift.xyz for project mail. Both with SPF, DKIM, and DMARC. I use email aliases extensively \u2014 every service gets a unique address.\n\n## Browser\n\nFirefox with uBlock Origin (hard mode), Privacy Badger, HTTPS Everywhere, Multi-Account Containers, and a custom user.js based on arkenfox. No Chrome. Ever.\n\n## VPN\n\nWireGuard tunnel to my VPS. All traffic routed through it on public networks. Dead simple config.\n\n## Passwords\n\nKeePassXC locally synced via Syncthing. No cloud password manager. The database is backed up encrypted to three locations.\n\n## Messaging\n\nSignal for anything sensitive. Discord for community (privacy settings maxed). Never SMS for anything important.\n\n## The Mindset\n\nPrivacy isn't just tools \u2014 it's habits:\n\n- **Compartmentalise** \u2014 Different emails, browsers, identities for different contexts\n- **Minimise** \u2014 Don't give data you don't have to\n- **Verify** \u2014 Check permissions, read privacy policies, audit your attack surface\n- **Update** \u2014 Patch everything, always\n\nStay safe out there.",
    "date": "2026-03-22",
    "difficulty": 2,
    "energy": 4,
    "excerpt": "A breakdown of the tools, habits, and configurations I use to stay private online \u2014 from DNS to email to browsing.",
    "focus": 5,
    "heart_rate": 65,
    "mood": "focused",
    "motivation": 4,
    "slug": "opsec-privacy-stack",
    "tags": [
      "privacy",
      "security",
      "opsec",
      "linux",
      "self-hosting"
    ],
    "threat_level": "MEDIUM",
    "time_written": "01:30",
    "title": "OpSec for the Paranoid: My Privacy Stack"
  },
  {
    "coffee": 5,
    "content": "## The Rabbit Hole\n\nIt started with a simple question: what if an AI could actually *do* things on my server, not just talk about doing them?\n\nI'd been messing around with LLMs for months \u2014 ChatGPT, Claude, local models on my GPU. They're brilliant at explaining things and writing snippets, but there's always that gap between 'here's the code' and 'it's actually running.' You still have to copy, paste, debug, fix the hallucinated import, debug again.\n\nThen I found Agent Zero.\n\n## What Is It?\n\nAgent Zero is an AI agent framework. You give it access to a terminal, a browser, memory, and tools. Then you tell it what you want. It figures out the steps, writes the code, runs it, checks the output, and iterates until it works.\n\nThe first time I watched it SSH into my VPS, install nginx, write a config file, test it, and reload the service \u2014 all without me touching the keyboard \u2014 I felt something between excitement and genuine unease.\n\n## The Good\n\n- It handles complex multi-step tasks that would take me an hour of googling\n- It remembers previous solutions and applies them to new problems\n- It can debug its own mistakes (most of the time)\n- It never gets frustrated at 3 AM\n\n## The Bad\n\n- It sometimes gets stuck in loops, trying the same failing approach repeatedly\n- Token costs add up fast on complex tasks\n- You need to be specific \u2014 vague instructions produce vague results\n- It occasionally decides to 'improve' things you didn't ask it to touch\n\n## The Philosophy\n\nThere's a real debate about whether giving AI tools like these is a good idea. I get it. But here's my take: these tools exist. They're getting better. The question isn't whether people will use them, it's whether you'll understand how they work when they become mainstream.\n\nI'd rather be the person who built with the early versions than the person trying to catch up later.\n\n## Current Setup\n\nMy Agent Zero instance runs in Docker on the VPS. It has access to the terminal, can browse the web, and maintains long-term memory. I use it for everything from server maintenance to building websites.\n\nYes, including this one.",
    "date": "2026-03-20",
    "difficulty": 4,
    "energy": 3,
    "excerpt": "What happens when you give an AI root access to your server and tell it to build things? Chaos, learning, and surprisingly good code.",
    "focus": 3,
    "heart_rate": 76,
    "id": 3,
    "mood": "locked-in",
    "motivation": 4,
    "slug": "ai-agents-3am",
    "tags": [
      "ai",
      "agent-zero",
      "automation",
      "development"
    ],
    "threat_level": "MED",
    "time_written": "03:22 AM",
    "title": "AI Agents at 3AM: Building Agent Zero",
    "word_count": 340
  },
  {
    "coffee": 4,
    "content": "# Terminal Ricing: My Kali Workflow in 2026\n\nI spend 90% of my time in a terminal. It better look good and work fast.\n\n## The Shell: Zsh + Oh My Zsh\n\nBash is fine. Zsh is better. With Oh My Zsh and the Powerlevel10k prompt, I get git branch/status, command execution time, Python virtualenv indicator, error code highlighting, and auto-suggestions from history.\n\n## Key Aliases\n\nMy .zshrc has about 200 lines of aliases and functions. Essentials: ll for detailed listing, gs/gp for git status/push, dc for docker compose, serve for quick http server, myip for external IP, ports for open connections.\n\n## Tmux\n\nAlways running with custom config: Ctrl+A prefix, mouse support, custom status bar with hostname/session/time, pipe for vertical split, dash for horizontal. Colours match the cyberpunk theme.\n\n## Tools I Can't Live Without\n\nfzf (fuzzy finder), bat (syntax-highlighted cat), ripgrep (fast grep), htop (process monitor), lazydocker (Docker TUI), nnn (file manager), jq (JSON processing).\n\n## Neovim\n\nRunning LazyVim with Treesitter, LSP for Python/JS/Go, Telescope, Which-key, and a custom cyberpunk colour scheme.\n\n## The Font\n\nJetBrains Mono with Nerd Font patches. Ligatures, readable at small sizes, all the icons Powerlevel10k needs.\n\n## Why It Matters\n\nA good terminal setup isn't just aesthetics \u2014 it's productivity. Every alias saves seconds. Every keybind saves keystrokes. Over thousands of commands a day, it adds up. Plus, it looks absolutely sick in screenshots.",
    "date": "2026-03-20",
    "difficulty": 1,
    "energy": 3,
    "excerpt": "A tour of my terminal setup \u2014 from Zsh to Tmux to custom aliases. Optimised for pentesting, development, and looking absolutely sick.",
    "focus": 3,
    "heart_rate": 62,
    "mood": "zen",
    "motivation": 4,
    "slug": "terminal-ricing-2026",
    "tags": [
      "linux",
      "terminal",
      "workflow",
      "customisation",
      "dotfiles"
    ],
    "threat_level": "LOW",
    "time_written": "22:15",
    "title": "Terminal Ricing: My Kali Workflow in 2026"
  },
  {
    "coffee": 5,
    "content": "## It Was Supposed To Be Quick\n\nFamous last words. All I wanted to do was update the nginx config to add a new subdomain. Five minutes, tops. I'd done it a hundred times before.\n\n```bash\nnginx -t && systemctl reload nginx\n```\n\nExcept this time, `nginx -t` returned an error I'd never seen. Something about a duplicate server name. I'd accidentally created a circular include that referenced itself through a symlink.\n\n## The Cascade\n\nIn my sleep-deprived wisdom, I decided to 'fix' it by removing what I thought was the duplicate config file. Turns out, that was the main config for jaeswift.xyz. Not the duplicate. The main one.\n\nNginx went down. All services went dark. Git, Plex, the docs site, everything. At 4 AM.\n\n## The Panic\n\nMy phone started buzzing. Uptime monitors screaming. I tried to restore from the backup. The backup was three days old because I'd been meaning to set up daily snapshots but hadn't got round to it.\n\nThree days of config changes, gone.\n\n## The Recovery\n\nI spent the next two hours reconstructing the nginx config from memory and bash history. `history | grep nginx` became my best friend. I found most of the server blocks in various terminal scrollback sessions.\n\nBy 6 AM, everything was back online. Mostly. The Plex config took another day to get right because I'd customised the proxy headers and couldn't remember the exact settings.\n\n## The Lessons\n\n1. **Backup your configs to git.** Not tomorrow. Now.\n2. **Never deploy after midnight.** Your brain is lying to you about how awake you are.\n3. **Test in staging.** I know, I know. But actually do it.\n4. **Document everything.** Past-you is the best resource for future-you, but only if past-you wrote things down.\n5. **Set up automated backups.** I now have hourly snapshots of all critical configs.\n\n## Silver Lining\n\nThe reconstructed config was actually cleaner than the original. Sometimes you need to burn it down to build it better.\n\nBut maybe not at 4 AM.",
    "date": "2026-03-15",
    "difficulty": 5,
    "energy": 1,
    "excerpt": "A war story about a production deploy that went sideways, the frantic debugging that followed, and what I learned from nuking my own DNS.",
    "focus": 2,
    "heart_rate": 112,
    "id": 4,
    "mood": "chaotic",
    "motivation": 2,
    "slug": "4am-deploy",
    "tags": [
      "devops",
      "war-story",
      "nginx",
      "lessons"
    ],
    "threat_level": "CRITICAL",
    "time_written": "04:13 AM",
    "title": "The 4AM Deploy: When Everything Goes Wrong",
    "word_count": 338
  }
]