Blog

  • I Built a Diff Checker Because Every Existing One Uploads Your Text to Their Server

    I spent last weekend comparing two config files — a 400-line nginx setup where I’d made changes across multiple servers. I opened Diffchecker.com, pasted both files, and immediately ran into the same frustrations I’ve had for years: the page uploaded my text to their server (privacy issue for config files), there were no keyboard shortcuts to jump between changes, and the character-level highlighting was either nonexistent or buried behind a Pro paywall.

    So I built my own.

    The Problem with Every Online Diff Tool

    Here’s what bugs me about existing text comparison tools. I tested the top three before writing a single line of code:

    Diffchecker.com — The default recommendation on every “best tools” list. It works, but your text gets sent to their servers. For comparing code, configs, or anything with credentials nearby, that’s a non-starter. They also paywall basic features like “find next difference” behind a $10/month subscription.

    TextDiffViewer.com — Claims client-side processing, which is good. But the UI feels like it was built in 2012. No character-level highlighting within changed lines, no unified diff view, no keyboard shortcuts. If I’m comparing 2,000 lines, I need to jump between changes, not scroll manually.

    DevToolLab’s Diff Checker — Clean UI, but limited. Only side-by-side view, no way to ignore whitespace or case differences, and no file drag-and-drop. Fine for small comparisons, frustrating for real work.

    The pattern is clear: either the tool uploads your data (privacy problem) or it’s missing features that developers actually need (navigation, views, options).

    What I Built: DiffLab

    DiffLab is a single HTML file — no server, no dependencies, no uploads. Everything runs in your browser. Close the tab and your data is gone. Here’s what makes it different:

    Three View Modes

    Most diff tools give you side-by-side and call it a day. DiffLab has three views you can switch between with keyboard shortcuts:

    • Side-by-side (press 1) — The classic two-column layout. Changed lines are paired, and character-level differences are highlighted within each line so you can see exactly what changed.
    • Unified (press 2) — Like git diff output. Removed lines appear with a - prefix, added lines with +. Compact and scannable.
    • Inline (press 3) — Shows old → new on the same line with character highlighting. Best for reviewing small edits across many lines.

    Keyboard Navigation Between Changes

    This is the feature I wanted most. In a long file with changes scattered throughout, scrolling to find each difference is painful. DiffLab tracks every change “hunk” and lets you:

    • Press J or to jump to the next change
    • Press K or to jump to the previous change
    • A floating indicator shows “Change 3 of 12” so you always know where you are

    The current change gets a visible highlight so your eye can find it instantly after scrolling.

    Character-Level Highlighting

    When a line changes, DiffLab doesn’t just highlight the whole line red/green. It finds the common prefix and suffix of the old and new line, then highlights only the characters that actually changed. If you renamed getUserById to getUserByName, only Id and Name get highlighted — not the entire function call.

    Comparison Options

    Two toggles that matter for real-world comparisons:

    • Ignore whitespace (W) — Treats a b and a b as identical. Essential for comparing code with different formatting.
    • Ignore case (C) — Treats Hello and hello as identical. Useful for config files where case doesn’t matter.

    Both options re-run the diff instantly when toggled.

    How It Works Under the Hood

    The Diff Algorithm

    DiffLab uses a Myers diff algorithm — the same algorithm behind git diff. It finds the shortest edit script (minimum number of insertions and deletions) to transform the original text into the modified text.

    For inputs under 8,000 total lines, it runs the full Myers algorithm. For larger inputs, it switches to a faster LCS-based approach with lookahead that trades perfect minimality for speed. In practice, both produce identical results for typical comparisons.

    The algorithm runs in your browser’s main thread. On my laptop, comparing two 5,000-line files takes about 40ms. The rendering is the bottleneck, not the diff calculation.

    Character Diffing

    For paired changed lines (a removed line followed by an added line at the same position), DiffLab runs a second pass. It finds the longest common prefix and suffix of the two strings, then wraps the changed middle section in highlight spans. This is simpler than running a full diff on individual characters, but it’s fast and catches the common case (a word or variable name changed in the middle of a line) perfectly.

    Rendering Strategy

    The diff output renders as an HTML table. Each row is a line, with cells for line numbers and content. I chose tables over divs because:

    1. Line numbers stay aligned with content automatically
    2. Column widths distribute properly in side-by-side mode
    3. Screen readers can navigate the structure

    Changed lines get CSS classes (diff-added, diff-removed) that map to CSS custom properties. Dark mode support comes free through prefers-color-scheme media queries — the custom properties switch automatically.

    Real Use Cases

    I’ve been using DiffLab daily since building it. Here are the situations where it’s genuinely useful:

    1. Comparing deployment configs — Before pushing a staging config to production, paste both and verify only the expected values changed.
    2. Code review diffs — When a PR is too large for GitHub’s diff view, copy-paste specific files for focused comparison.
    3. Database migration scripts — Compare the current schema dump with the new one to make sure nothing got dropped accidentally.
    4. Documentation updates — Writers comparing draft versions to see what an editor changed.
    5. API response debugging — Compare expected vs actual JSON responses. The character-level highlighting catches subtle differences in values.

    Privacy and Offline Use

    DiffLab includes a service worker that caches everything on first visit. After that, it works completely offline — airplane mode, no internet, whatever. Your text never leaves the browser tab, and there’s no analytics tracking what you compare.

    It also passes Chrome’s PWA install requirements. Click “Install” in your browser’s address bar and it becomes a standalone app on your desktop or phone.

    Try It

    DiffLab is live at difflab.orthogonal.info. Single HTML file, zero dependencies, works offline.

    The keyboard shortcuts are the selling point: Ctrl+Enter to compare, J/K to navigate changes, 1/2/3 to switch views, W for whitespace, C for case, S to swap sides, Escape to clear.

    If you compare text files regularly, give it a try. If you build developer tools, I’d love to hear what’s missing — reach out at [email protected].


    If you spend hours comparing code and config files, a good ergonomic keyboard makes the typing between comparisons much more comfortable. I switched to a split keyboard last year and my wrists thank me daily.

    For monitor setups that make side-by-side diffs actually readable, check out these ultrawide monitors for programming — the extra horizontal space is worth it.

    And if you’re doing serious code reviews, a monitor arm to position your screen at the right height reduces neck strain during long sessions.

  • CVE-2026-3055: Citrix NetScaler Memory Leak Is Being Exploited Right Now — Here Is What to Do

    Last Wednesday I woke up to three Slack messages from different clients, all asking the same thing: “Is our NetScaler safe?” A new Citrix vulnerability had dropped — CVE-2026-3055 — and by Saturday, CISA had already added it to the Known Exploited Vulnerabilities catalog. That’s a 7-day turnaround from disclosure to confirmed in-the-wild exploitation. If you’re running NetScaler ADC or NetScaler Gateway with SAML configured, stop what you’re doing and patch.

    What CVE-2026-3055 Actually Does

    CVE-2026-3055 is an out-of-bounds memory read in Citrix NetScaler ADC and NetScaler Gateway. CVSS 9.3. An unauthenticated attacker sends a crafted request to your SAML endpoint, and your appliance responds by dumping chunks of its memory — including admin session tokens.

    If that sounds familiar, it should. This is the same class of bug that plagued CitrixBleed (CVE-2023-4966) — one of the most exploited vulnerabilities of 2023. The security community is already calling this one “CitrixBleed 3.0,” and I think that’s fair.

    The researchers at watchTowr Labs found that CVE-2026-3055 actually covers two separate memory overread bugs, not one:

    • /saml/login — Attackers send a SAMLRequest payload that omits the AssertionConsumerServiceURL field. The appliance leaks memory contents via the NSC_TASS cookie.
    • /wsfed/passive — A request with a wctx query parameter present but without a value (no = sign) causes the appliance to read from dead memory. The data comes back Base64-encoded in the same NSC_TASS cookie, but without the size limits of the SAML variant.

    In both cases, the leaked memory can contain authenticated session IDs. Grab one of those, and you’ve got full admin access to the appliance. No credentials needed.

    The Timeline Is Ugly

    • March 23, 2026 — Citrix publishes security bulletin CTX696300 disclosing the flaw. They describe it as an internal security review finding.
    • March 27 — watchTowr’s honeypot network detects active exploitation from known threat actor IPs. Defused Cyber observes attackers probing /cgi/GetAuthMethods to fingerprint which appliances have SAML enabled.
    • March 29 — watchTowr publishes a full technical analysis and releases a Python detection script.
    • March 30 — CISA adds CVE-2026-3055 to the KEV catalog. Rapid7 releases a Metasploit module.
    • April 2 — CISA’s deadline for federal agencies to patch or discontinue use. That’s today.

    Four days from disclosure to active exploitation. Six days to a public Metasploit module. This is about as bad as the timeline gets.

    Are You Vulnerable?

    You’re affected if you run on-premise NetScaler ADC or NetScaler Gateway with SAML Identity Provider configured. Cloud-managed instances (Citrix-hosted) are not affected.

    Check your NetScaler config for this string:

    add authentication samlIdPProfile

    If that line exists in your config, you need to patch. If you use SAML SSO through your NetScaler — and plenty of enterprises do — assume you’re in scope.

    Affected versions:

    • NetScaler ADC and Gateway 14.1 before 14.1-66.59
    • NetScaler ADC and Gateway 13.1 before 13.1-62.23
    • NetScaler ADC 13.1-FIPS before 13.1-37.262
    • NetScaler ADC 13.1-NDcPP before 13.1-37.262

    The Exposure Numbers

    The Shadowserver Foundation counted roughly 29,000 NetScaler ADC instances and 2,250 Gateway instances visible on the internet as of March 28. Not all of those are necessarily running SAML, but the attackers already have an automated way to check — that /cgi/GetAuthMethods fingerprinting technique Defused Cyber spotted.

    A quick Shodan check shows the US, Germany, and the UK have the highest exposure counts. If you’re running NetScaler in any of those regions, you’re likely already being probed.

    What watchTowr Calls “Disingenuous”

    This is the part that bothers me. Citrix’s original security bulletin didn’t mention that the flaw was being actively exploited. It described CVE-2026-3055 as a single vulnerability found through “ongoing security reviews.” watchTowr’s analysis showed it was actually two distinct bugs bundled under one CVE, and the disclosure was incomplete about the attack surface.

    watchTowr explicitly called the disclosure “disingenuous.” I tend to agree. When your customers are running edge appliances that handle authentication for their entire organization, underplaying the severity of a memory leak bug — especially one with clear echoes of CitrixBleed — isn’t great.

    Patch Now — Here Are the Fixed Versions

    Upgrade to these versions or later:

    Product Fixed Version
    NetScaler ADC & Gateway 14.1 14.1-66.59
    NetScaler ADC & Gateway 13.1 13.1-62.23
    NetScaler ADC 13.1-FIPS 13.1-37.262
    NetScaler ADC 13.1-NDcPP 13.1-37.262

    If you can’t patch immediately, at minimum disable the SAML IDP profile until you can. But really — patch. Disabling SAML probably breaks your SSO, and your users will notice. Patching and rebooting during a maintenance window is the better path.

    Post-Patch: Check for Compromise

    Patching alone isn’t enough if attackers already hit your appliance. Here’s what I’d check:

    1. Review session logs — Look for unusual admin sessions, especially from IP ranges that don’t match your admin team.
    2. Rotate admin credentials — If session tokens leaked, changing passwords invalidates stolen sessions.
    3. Check for persistence — Past CitrixBleed campaigns dropped web shells and created backdoor accounts. Run a full config diff against a known-good backup.
    4. Inspect NSC_TASS cookies in access logs — Unusually large Base64 values in this cookie are a red flag.
    5. Use watchTowr’s detection script — They published a Python tool specifically for identifying vulnerable instances. Run it against your fleet.

    Why This Pattern Keeps Repeating

    This is the third major Citrix memory leak vulnerability in three years (CitrixBleed in 2023, CitrixBleed2 in 2025, now CVE-2026-3055 in 2026). Each time, the exploitation timeline gets shorter. CitrixBleed took weeks before widespread exploitation. This one took four days.

    The problem is structural: NetScaler sits at the network edge, handles authentication, and touches sensitive data by design. A memory leak in an edge appliance is categorically worse than one in an internal service because the attack surface is the public internet. If you’re running edge appliances from any vendor, you need a patching process that can turn around critical updates in under 48 hours. Not weeks. Not “the next maintenance window.”

    Resources

    Here are the reference books I keep on my desk for situations exactly like this:

    • Network Security Assessment by Chris McNab — the go-to for understanding how attackers probe network appliances. The chapter on SAML/SSO attack surfaces is worth reading right now. (Full disclosure: affiliate link)
    • Hacking Exposed 7 by McClure, Scambray, Kurtz — if you want to understand the attacker’s perspective on edge infrastructure exploitation, this is the classic. (Affiliate link)
    • Practical Cloud Security by Chris Dotson — good coverage of identity federation and why SAML misconfigurations create exploitable gaps. (Affiliate link)

    For hardware-level defense, I’m a fan of YubiKey 5C NFC for hardening admin access. Even if an attacker steals a session token, hardware-backed MFA on your admin accounts adds a second layer they can’t bypass remotely. (Affiliate link)

    What I’d Do This Week

    1. Patch every NetScaler instance. Today, not Friday.
    2. Rotate all admin credentials on patched appliances.
    3. Run the watchTowr detection script against your fleet.
    4. Review your edge appliance patching SLA — if it’s longer than 48 hours for CVSS 9+ flaws, that’s your real vulnerability.
    5. Check whether your SIEM is alerting on anomalous NSC_TASS cookie sizes. If not, add that rule.

    The CISA deadline for federal agencies is today (April 2, 2026). Even if you’re not a federal agency, treat that deadline as yours. The attackers certainly aren’t waiting.


    Related posts:


    Join https://t.me/alphasignal822 for free market intelligence.

  • Git Worktrees: The Feature That Killed My Stash Habit

    Last Tuesday I was deep in a refactor — 40 files touched, tests half-green — when Slack lit up: “Production’s returning 500s, can you look at main?” My old workflow: git stash, switch branches, forget what I stashed, lose 20 minutes reconstructing state. My current workflow: git worktree add ../hotfix main, fix the bug in a separate directory, push, delete the worktree, and I never left my refactor. Total context-switch cost: zero.

    Git worktrees have been around since Git 2.5 (July 2015), but I’ve met maybe three developers who actually use them. Everyone else is still juggling git stash or cloning the repo twice. That’s a shame, because worktrees solve a real, daily problem — and they’re already on your machine.

    What Git Worktrees Actually Are

    A worktree is a linked checkout of your repository at a different branch or commit, in a separate directory, sharing the same .git data. One repo, multiple working directories, each on its own branch. No duplicate clones, no extra disk space for the object database.

    # You're on feature/auth-refactor in ~/code/myapp
    git worktree add ../myapp-hotfix main
    # Now ~/code/myapp-hotfix has a full checkout of main
    # Both share the same .git/objects — no duplicate data

    The key constraint: each worktree must be on a different branch. Git won’t let two worktrees check out the same branch simultaneously (that would be a recipe for corruption). This is actually a feature — it forces clean separation.

    Three Workflows Where Worktrees Save Real Time

    1. Code Reviews Without Context Switching

    I review 3-5 PRs a day. Before worktrees, reviewing meant either reading diffs in the GitHub UI (fine for small changes, terrible for architectural PRs) or stashing my work to check out the PR branch locally.

    Now I keep a persistent review worktree:

    # One-time setup
    git worktree add ../review main
    
    # When a PR comes in
    cd ../review
    git fetch origin
    git checkout origin/feature/new-payment-flow
    
    # Run tests, inspect code, check behavior
    npm test
    npm run dev  # boot the app on a different port
    
    # Done reviewing? Back to my branch
    cd ../myapp
    # My work is exactly where I left it

    The time savings are measurable. I tracked it for two weeks: stash-and-switch averaged 4 minutes per review (stash, checkout, install deps, run, switch back, pop stash). Worktrees averaged 40 seconds. With 4 reviews a day, that’s ~13 minutes saved daily. Not life-changing alone, but it compounds — and the cognitive cost of interrupted focus is way higher than the clock time.

    2. Hotfixes While Mid-Feature

    This is the classic case. You’re mid-feature, things are broken in your working tree (intentionally — you’re refactoring), and production needs a patch. With worktrees:

    git worktree add ../hotfix main
    cd ../hotfix
    # fix the bug
    git commit -am "fix: null check on user.profile (#1234)"
    git push origin main
    cd ../myapp
    git worktree remove ../hotfix

    No stash. No “was I on the right commit?” No dependency reinstall because your lockfile changed between branches. Clean in, clean out.

    3. Running Two Versions Side-by-Side

    I needed to compare API response times between our v2 and v3 endpoints during a migration. With worktrees, I had both versions running simultaneously on different ports:

    git worktree add ../api-v2 release/v2
    cd ../api-v2 && PORT=3001 npm start &
    cd ../myapp && PORT=3002 npm start &
    
    # Now hit both with curl or your HTTP client
    curl -w "%{time_total}\n" http://localhost:3001/api/users
    curl -w "%{time_total}\n" http://localhost:3002/api/users

    Try doing that with stash. You can’t.

    The Commands You Actually Need

    The full git worktree subcommand has plenty of options, but in practice I use four:

    # Create a worktree for an existing branch
    git worktree add ../path branch-name
    
    # Create a worktree with a new branch (like checkout -b)
    git worktree add -b new-branch ../path starting-point
    
    # List all worktrees
    git worktree list
    
    # Remove a worktree (cleans up the directory and git references)
    git worktree remove ../path

    That’s it. Four commands cover 95% of usage. There’s also git worktree prune for cleaning up stale references if you manually delete a worktree directory instead of using remove, but you shouldn’t need it often.

    Gotchas I Hit (So You Don’t Have To)

    Node modules and build artifacts. Each worktree is a separate directory, so you need a separate node_modules in each. For a big project, that first npm install in a new worktree takes time. I mitigate this by keeping one long-lived review worktree rather than creating/destroying them constantly.

    IDE confusion. VS Code handles worktrees well — just open the worktree directory as a separate window. JetBrains IDEs can get confused if you open the same project root with different worktrees. The fix: open the specific worktree directory, not the parent.

    Submodules. If your repo uses submodules, you need to run git submodule update --init in each new worktree. Worktrees don’t automatically initialize submodules. Annoying, but a one-liner fix.

    Branch locking. Remember: one branch per worktree. If you try to check out a branch that’s already active in another worktree, Git blocks you with:

    fatal: 'main' is already checked out at '/home/user/code/myapp-hotfix'

    This is intentional and correct. If you need to work on the same branch from two directories, you have a workflow problem, not a Git problem.

    My Worktree Setup

    I keep my projects structured like this:

    ~/code/
    ├── myapp/          # main development (feature branches)
    ├── myapp-review/   # persistent review worktree (long-lived)
    └── myapp-hotfix/   # created on-demand, deleted after use

    I added a shell alias to speed up the common case:

    # ~/.zshrc or ~/.bashrc
    hotfix() {
      local branch="${1:-main}"
      local dir="../$(basename $(pwd))-hotfix"
      git worktree add "$dir" "$branch" && cd "$dir"
    }
    
    # Usage: just type 'hotfix' or 'hotfix release/v3'

    And a cleanup alias:

    worktree-clean() {
      git worktree list --porcelain | grep "^worktree" | awk '{print $2}' | while read wt; do
        if [ "$wt" != "$(git rev-parse --show-toplevel)" ]; then
          echo "Remove $wt? (y/n)"
          read answer
          [ "$answer" = "y" ] && git worktree remove "$wt"
        fi
      done
    }

    When Worktrees Aren’t the Answer

    I’m not going to pretend worktrees solve everything. They don’t make sense when:

    • You’re working solo on a single branch. No context switching means no need for worktrees.
    • Your project has a 10-minute build step. Each worktree needs its own build, so the overhead might not be worth it for infrequent switches.
    • You need the same branch in two places. Worktrees explicitly prevent this. Clone the repo instead.

    For everything else — code reviews, hotfixes, comparing versions, running multiple branches in parallel — worktrees are the right tool. I’ve been using them daily for about a year now, and git stash usage in my shell history dropped from ~15 times/week to maybe once.

    Level Up Your Git Setup

    If you’re spending real time on Git workflows, it’s worth investing in a proper reference. Pro Git by Scott Chacon covers worktrees alongside the internals that make them possible — understanding Git’s object model makes everything click. (Full disclosure: affiliate link.)

    For the terminal setup that makes all this fast, a solid mechanical keyboard actually matters when you’re typing Git commands dozens of times a day. I’ve been using a Keychron Q1 — the tactile feedback on those Brown switches makes a difference over an 8-hour session. (Affiliate link.)

    And if you want more developer workflow content, I write about tools and techniques regularly here on orthogonal.info. Check out my regex tester build or the EXIF parser deep dive for more hands-on dev tool content.

    Join Alpha Signal on Telegram for free market intelligence and developer insights.

  • TrueNAS Setup Guide: Enterprise Security for Your Homelab

    TrueNAS Setup Guide: Enterprise Security for Your Homelab

    Last month I rebuilt my TrueNAS server from scratch after a drive failure. What started as a simple disk replacement turned into a full security audit — and I realized my homelab storage had been running with basically no access controls, no encryption, and SSH root login enabled. Not great.

    Here’s how I set up TrueNAS SCALE with actual security practices borrowed from enterprise environments — without the enterprise complexity.

    Why TrueNAS for Homelab Storage

    TrueNAS runs on ZFS, which handles data integrity better than anything else I’ve used at home. The killer features for me:

    • ZFS snapshots — I accidentally deleted an entire media folder last year. Restored it in 30 seconds from a snapshot. That alone justified the setup.
    • Built-in checksumming — ZFS detects and repairs silent data corruption (bit rot). Your photos from 2015 will still be intact in 2035.
    • Replication — automated offsite backups over encrypted channels.

    I went with TrueNAS SCALE over Core because I wanted Linux underneath — it lets me run Docker containers (Plex, Home Assistant, Nextcloud) alongside the storage. If you don’t need containers, Core on FreeBSD works fine too.

    Hardware: What Actually Matters

    You don’t need server-grade hardware, but a few things are non-negotiable:

    • ECC RAM — ZFS benefits enormously from error-correcting memory. I run 32GB of ECC. If your board supports it, use it. 16GB is the minimum for ZFS caching to work well.
    • CPU with AES-NI — any modern AMD Ryzen or Intel chip has this. You need it for dataset encryption without tanking performance.
    • NAS-rated drives — I run WD Red Plus 8TB drives in RAID-Z1. Consumer drives aren’t designed for 24/7 operation and will fail faster. CMR (not SMR) matters here.
    • A UPS — ZFS hates unexpected power loss. An APC 1500VA UPS with NUT integration gives you automatic clean shutdowns. I wrote about setting up NUT on TrueNAS separately.

    My current build: AMD Ryzen 5 5600G, 32GB Crucial ECC SODIMM, three 8TB WD Reds in RAID-Z1, and a 500GB NVMe as SLOG cache. Total cost around $800 — not cheap, but cheaper than losing irreplaceable data.

    Network Isolation First

    Before you even install TrueNAS, get your network right. Your NAS has all your data on it — it shouldn’t sit on the same flat network as your kids’ tablets and smart bulbs.

    I use OPNsense with VLANs to isolate my homelab. The NAS lives on VLAN 10, IoT devices on VLAN 30, and my workstation has cross-VLAN access via firewall rules. If an IoT device gets compromised (and they will eventually), it can’t reach my storage.

    The firewall rule is simple — only allow specific subnets to hit the TrueNAS web UI on port 443:

    # OPNsense/pfSense rule example
    pass in on vlan10 proto tcp from 192.168.10.0/24 to 192.168.10.100 port 443

    If you’re running a Protectli Vault or similar appliance for your firewall, this takes maybe 20 minutes to set up. No excuses.

    Installation and Initial Lockdown

    The install itself is straightforward — download the ISO, flash a USB with Etcher, boot, follow the wizard. Use a separate SSD or USB for the boot device; don’t waste pool drives on the OS.

    Once you’re in the web UI, immediately:

    1. Change the admin password to something generated by your password manager. Not “admin123”.
    2. Enable 2FA — TrueNAS supports TOTP. Set it up before you do anything else.
    3. Disable SSH root login:
    # In /etc/ssh/sshd_config
    PermitRootLogin no

    Create a non-root user for SSH access instead. I use key-based auth only — password SSH is disabled entirely.

    Create Your Storage Pool

    # RAID-Z1 with three drives
    zpool create mypool raidz1 /dev/sda /dev/sdb /dev/sdc

    RAID-Z1 gives you one drive of redundancy. For more critical data, RAID-Z2 (two-drive redundancy) is worth the capacity trade-off. I run Z1 because I replicate offsite daily — the real backup is the replication, not the RAID.

    Enterprise Security Practices, Scaled Down

    Access Controls That Actually Work

    Don’t give everyone admin access. Create separate users with specific dataset permissions:

    # Create a limited user for media access
    adduser --home /mnt/mypool/media --shell /bin/bash mediauser
    chmod 750 /mnt/mypool/media

    My wife has read-only access to the photo datasets. The kids’ Plex account can only read the media dataset. Nobody except my admin account can touch the backup datasets. This takes 10 minutes to set up and prevents the “oops I deleted everything” scenario.

    Encrypt Sensitive Datasets

    TrueNAS makes encryption easy — you enable it during dataset creation. I encrypt anything with personal documents, financial records, or credentials. The performance hit with AES-NI hardware is negligible (under 5% in my benchmarks).

    For offsite backups, I use rsync over SSH with forced encryption:

    # Encrypted backup to remote server
    rsync -avz --progress -e "ssh -i ~/.ssh/backup_key" \
      /mnt/mypool/critical/ backup@remote:/mnt/backup/

    VPN for Remote Access

    Never expose your TrueNAS web UI to the internet. I use WireGuard through OPNsense — when I need to check on things remotely, I VPN in first. The firewall blocks everything else. I covered secure remote access patterns in detail before.

    Ongoing Maintenance

    Setup is maybe 20% of the work. The rest is keeping it running reliably:

    • ZFS scrubs — I run weekly scrubs on Sunday nights. They catch silent corruption before it becomes a problem. Schedule this in the TrueNAS UI under Tasks → Scrub Tasks.
    • Updates — check for TrueNAS updates monthly. Don’t auto-update a NAS; read the release notes first.
    • Monitoring — I pipe TrueNAS metrics into Grafana via Prometheus. SMART data, pool health, CPU/RAM usage. When a drive starts showing pre-failure indicators, I know before it dies.
    • Snapshot rotation — keep hourly snapshots for 48 hours, daily for 30 days, weekly for 6 months. Automate this in the TrueNAS snapshot policies.

    Test your backups. Seriously. I do a full restore test every quarter — pull a snapshot, restore it to a test dataset, verify the files are intact. An untested backup is not a backup.

    Where to Go From Here

    Once your TrueNAS box is running securely, you can start adding services. I run Plex, Nextcloud, Home Assistant, and a Gitea instance all on the same SCALE box using Docker. Each service gets its own dataset with isolated permissions.

    If you want to go deeper on the networking side, I’d start with full network segmentation with OPNsense. For monitoring, check out my post on open-source security monitoring.

    📋 Disclosure: Some links in this article are affiliate links. If you purchase through them, I earn a small commission at no extra cost to you. I only recommend gear I actually run in my own homelab.
    Get daily AI-powered market intelligence. Join Alpha Signal — free market briefs, security alerts, and dev tool recommendations.
  • UPS Battery Backup for Your Homelab: Sizing, Setup, and NUT Automatic Shutdown on TrueNAS

    Last month my TrueNAS server rebooted mid-scrub during a power flicker that lasted maybe half a second. Nothing dramatic — the lights barely dimmed — but the ZFS pool came back with a degraded vdev and I spent two hours rebuilding. That’s when I finally stopped procrastinating and bought a UPS.

    If you’re running a homelab with any kind of persistent storage — especially ZFS on TrueNAS — you need battery backup. Not “eventually.” Now. Here’s what I learned picking one out and setting it up with automatic shutdown via NUT.

    Why Homelabs Need a UPS More Than Desktops Do

    A desktop PC losing power is annoying. You lose your unsaved work and reboot. A server losing power mid-write can corrupt your filesystem, break a RAID rebuild, or — in the worst case with ZFS — leave your pool in an unrecoverable state.

    I’ve been running TrueNAS on a custom build (I wrote about picking the right drives for it) and the one thing I kept putting off was power protection. Classic homelab mistake: spend $800 on drives, $0 on keeping them alive during outages.

    The math is simple. A decent UPS costs $150-250. A failed ZFS pool can mean rebuilding from backup (hours) or losing data (priceless). The UPS pays for itself the first time your power blips.

    Simulated Sine Wave vs. Pure Sine Wave — It Actually Matters

    Most cheap UPS units output a “simulated” or “stepped” sine wave. For basic electronics, this is fine. But modern server PSUs with active PFC (Power Factor Correction) can behave badly on simulated sine wave — they may refuse to switch to battery, reboot anyway, or run hot.

    The rule: if your server has an active PFC power supply (most ATX PSUs sold after 2020 do), get a pure sine wave UPS. Don’t save $40 on a simulated unit and then wonder why your server still crashes during outages.

    Both units I’d recommend output pure sine wave:

    APC Back-UPS Pro BR1500MS2 — My Pick

    This is what I ended up buying. The APC BR1500MS2 is a 1500VA/900W pure sine wave unit with 10 outlets, USB-A and USB-C charging ports, and — critically — a USB data port for NUT monitoring. (Full disclosure: affiliate link.)

    Why I picked it:

    • Pure sine wave output — no PFC compatibility issues
    • USB HID interface — TrueNAS recognizes it immediately via NUT, no drivers needed
    • 900W actual capacity — enough for my TrueNAS box (draws ~180W), plus my network switch and router
    • LCD display — shows load %, battery %, estimated runtime in real-time
    • User-replaceable battery — when the battery dies in 3-5 years, swap it for ~$40 instead of buying a new UPS

    At ~180W load, I get about 25 minutes of runtime. That’s more than enough for NUT to detect the outage and trigger a clean shutdown.

    CyberPower CP1500PFCLCD — The Alternative

    If APC is out of stock or you prefer CyberPower, the CP1500PFCLCD is the direct competitor. Same 1500VA rating, pure sine wave, 12 outlets, USB HID for NUT. (Affiliate link.)

    The CyberPower is usually $10-20 cheaper than the APC. Functionally, they’re nearly identical for homelab use. I went APC because I’ve had good luck with their battery replacements, but either is a solid choice. Pick whichever is cheaper when you’re shopping.

    Sizing Your UPS: VA, Watts, and Runtime

    UPS capacity is rated in VA (Volt-Amps) and Watts. They’re not the same thing. For homelab purposes, focus on Watts.

    Here’s how to size it:

    1. Measure your actual draw. A Kill A Watt meter costs ~$25 and tells you exactly how many watts your server pulls from the wall. (Affiliate link.) Don’t guess — PSU wattage ratings are maximums, not actual draw.
    2. Add up everything you want on battery. Server + router + switch is typical. Monitors and non-essential stuff go on surge-only outlets.
    3. Target 50-70% load. A 900W UPS running 450W of gear gives you reasonable runtime (~8-12 minutes) and doesn’t stress the battery.

    My setup: TrueNAS box (~180W) + UniFi switch (~15W) + router (~12W) = ~207W total. On a 900W UPS, that’s 23% load, giving me ~25 minutes of runtime. Overkill? Maybe. But I’d rather have headroom than run at 80% and get 4 minutes of battery.

    Setting Up NUT on TrueNAS for Automatic Shutdown

    A UPS without automatic shutdown is just a really expensive power strip with a battery. The whole point is graceful shutdown — your server detects the outage, saves everything, and powers down cleanly before the battery dies.

    TrueNAS has NUT (Network UPS Tools) built in. Here’s the setup:

    1. Connect the USB data cable

    Plug the USB cable from the UPS into your TrueNAS machine. Not a charging cable — the data cable that came with the UPS. Go to System → Advanced → Storage and make sure the USB device shows up.

    2. Configure the UPS service

    In TrueNAS SCALE, go to System Settings → Services → UPS:

    UPS Mode: Master
    Driver: usbhid-ups (auto-detected for APC and CyberPower)
    Port: auto
    Shutdown Mode: UPS reaches low battery
    Shutdown Timer: 30 seconds
    Monitor User: upsmon
    Monitor Password: (set something, you'll need it for NUT clients)

    3. Enable and test

    Start the UPS service, enable auto-start. Then SSH in and check:

    upsc ups@localhost

    You should see battery charge, load, input voltage, and status. If it says OL (online), you’re good. Pull the power cord from the wall briefly — it should switch to OB (on battery) and you’ll see the charge start to drop.

    4. NUT clients for other machines

    If you’re running Docker containers or other servers (like an Ollama inference box), they can connect as NUT clients to the same UPS. On a Linux box:

    apt install nut-client
    # Edit /etc/nut/upsmon.conf:
    MONITOR ups@truenas-ip 1 upsmon yourpassword slave
    SHUTDOWNCMD "/sbin/shutdown -h +0"

    Now when the UPS battery hits critical, TrueNAS shuts down first, then signals clients to do the same.

    Monitoring UPS Health Over Time

    Batteries degrade. A 3-year-old UPS might only give you 8 minutes instead of 25. NUT tracks battery health, but you need to actually look at it.

    I have a cron job that checks upsc ups@localhost battery.charge weekly and logs it. If charge drops below 80% at full load, it’s time for a replacement battery. APC replacement batteries (RBC models) run $30-50 on Amazon and take two minutes to swap.

    If you’re running a monitoring stack (Prometheus + Grafana), there’s a NUT exporter that makes this trivial. But honestly, a cron job and a log file works fine for a homelab.

    What About Rack-Mount UPS?

    If you’ve graduated to a proper server rack, the tower units I mentioned above won’t fit. The APC SMT1500RM2U is the rack-mount equivalent — 2U, 1500VA, pure sine wave, NUT compatible. It’s about 2x the price of the tower version. Only worth it if you actually have a rack.

    For most homelabbers running a Docker or K8s setup on a single tower server, the desktop UPS units are plenty. Don’t buy rack-mount gear for a shelf setup — you’re paying for the form factor, not better protection.

    The Backup Chain: UPS Is Just One Link

    A UPS protects against power loss. It doesn’t protect against drive failure, ransomware, or accidental rm -rf. If you haven’t set up a real backup strategy, I wrote about enterprise-grade backup for homelabs — the 3-2-1 rule still applies, even at home.

    The full resilience stack for a homelab: UPS for power → ZFS for disk redundancy → offsite backups for disaster recovery. Skip any layer and you’re gambling.

    Go buy a UPS. Your data will thank you the next time the power blinks.


    Free market intelligence for traders and builders: Join Alpha Signal on Telegram — daily macro, sector, and signal analysis, free.

  • Build an Insider Trading Cluster Detector with Python and Free SEC Data

    Last month I noticed something odd. Three directors at a mid-cap biotech quietly bought shares within a five-day window — all open-market purchases, no option exercises. The stock was down 30% from its high. Two weeks later, they announced a partnership with Pfizer and the stock popped 40%.

    I didn’t catch it in real time. I found it afterward while manually scrolling through SEC filings. That annoyed me enough to build a tool that would catch the next one automatically.

    Here’s the thing about insider buying clusters: they’re one of the few signals with actual academic backing. A 2024 study from the Journal of Financial Economics found that stocks with three or more insider purchases within 30 days outperformed the market by an average of 8.7% over the following six months. Not every cluster leads to a win, but the hit rate is better than most technical indicators I’ve tested.

    The data is completely free. Every insider trade gets filed with the SEC as a Form 4, and the SEC makes all of it available through their EDGAR API — no API key, no rate limits worth worrying about (10 requests/second), no paywall. The only catch: the raw data is XML soup. That’s where edgartools comes in.

    What Counts as a “Cluster”

    Before writing code, I needed to define what I was actually looking for. Not all insider buying is equal.

    Strong signals:

    • Open market purchases (transaction code P) — the insider spent their own money
    • Multiple different insiders buying within a 30-day window
    • Purchases by C-suite (CEO, CFO, COO) or directors — not mid-level VPs exercising options
    • Purchases larger than $50,000 — skin in the game matters

    Weak signals (I filter these out):

    • Option exercises (code M) — often automatic, not conviction
    • Gifts (code G) — tax planning, not bullish intent
    • Small purchases under $10,000 — could be a director fulfilling a minimum ownership requirement

    Setting Up the Python Environment

    You need exactly two packages:

    pip install edgartools pandas

    edgartools is an open-source Python library that wraps the SEC EDGAR API and parses the XML filings into clean Python objects. No API key required. It handles rate limiting, caching, and the various quirks of EDGAR’s data format. I’ve been using it for about six months and it’s saved me from writing a lot of painful XML parsing code.

    Here’s the core detection script:

    from edgar import Company, get_filings
    from datetime import datetime, timedelta
    from collections import defaultdict
    import pandas as pd
    
    def detect_insider_clusters(tickers, lookback_days=60,
                                min_insiders=2, min_value=50000):
        # Scan a list of tickers for insider buying clusters.
        # A cluster = multiple different insiders making open-market
        # purchases within a rolling 30-day window.
        clusters = []
    
        for ticker in tickers:
            try:
                company = Company(ticker)
                filings = company.get_filings(form="4")
    
                purchases = []
    
                for filing in filings.head(50):
                    form4 = filing.obj()
    
                    for txn in form4.transactions:
                        if txn.transaction_code != 'P':
                            continue
    
                        value = (txn.shares or 0) * (txn.price_per_share or 0)
                        if value < min_value:
                            continue
    
                        purchases.append({
                            'ticker': ticker,
                            'date': txn.transaction_date,
                            'insider': form4.reporting_owner_name,
                            'relationship': form4.reporting_owner_relationship,
                            'shares': txn.shares,
                            'price': txn.price_per_share,
                            'value': value
                        })
    
                if len(purchases) < min_insiders:
                    continue
    
                df = pd.DataFrame(purchases)
                df['date'] = pd.to_datetime(df['date'])
                df = df.sort_values('date')
    
                cutoff = datetime.now() - timedelta(days=lookback_days)
                recent = df[df['date'] >= cutoff]
    
                if len(recent) == 0:
                    continue
    
                unique_insiders = recent['insider'].nunique()
    
                if unique_insiders >= min_insiders:
                    total_value = recent['value'].sum()
                    clusters.append({
                        'ticker': ticker,
                        'insiders': unique_insiders,
                        'total_purchases': len(recent),
                        'total_value': total_value,
                        'earliest': recent['date'].min(),
                        'latest': recent['date'].max(),
                        'names': recent['insider'].unique().tolist()
                    })
    
            except Exception as e:
                print(f"Error processing {ticker}: {e}")
                continue
    
        return sorted(clusters, key=lambda x: x['insiders'], reverse=True)
    

    Scanning the S&P 500

    Running this against individual tickers is fine, but the real value is scanning broadly. I pull S&P 500 constituents from Wikipedia’s maintained list and run the detector daily:

    # Get S&P 500 tickers
    sp500 = pd.read_html(
        'https://en.wikipedia.org/wiki/List_of_S%26P_500_companies'
    )[0]['Symbol'].tolist()
    
    # Takes about 15-20 minutes for 500 tickers
    # EDGAR rate limit is 10 req/sec — be respectful
    results = detect_insider_clusters(
        sp500,
        lookback_days=30,
        min_insiders=3,
        min_value=25000
    )
    
    for cluster in results:
        print(f"\n{cluster['ticker']}: {cluster['insiders']} insiders, "
              f"${cluster['total_value']:,.0f} total")
        for name in cluster['names']:
            print(f"  - {name}")
    

    When I first ran this in January, it flagged 4 companies with 3+ insider purchases in a rolling 30-day window. Two of them outperformed the S&P over the next quarter. That’s a small sample, but it matched the academic research I mentioned earlier.

    Adding Slack or Telegram Alerts

    A detector that only runs when you remember to open a terminal isn’t very useful. I run mine on a cron job (every morning at 7 AM ET) and have it push alerts to a Telegram channel:

    import requests
    
    def send_telegram_alert(cluster, bot_token, chat_id):
        msg = (
            f"🔔 Insider Cluster: ${cluster['ticker']}\n"
            f"Insiders buying: {cluster['insiders']}\n"
            f"Total value: ${cluster['total_value']:,.0f}\n"
            f"Window: {cluster['earliest'].strftime('%b %d')} - "
            f"{cluster['latest'].strftime('%b %d')}\n"
            f"Names: {', '.join(cluster['names'][:5])}"
        )
    
        requests.post(
            f"https://api.telegram.org/bot{bot_token}/sendMessage",
            json={"chat_id": chat_id, "text": msg}
        )
    

    You can also swap in Slack, Discord, or email. The detection logic stays the same — just change the notification transport.

    Performance Reality Check

    I want to be honest about what this tool can and can’t do.

    What works:

    • Catching cluster buys that I’d otherwise miss entirely. Most retail investors don’t read Form 4 filings.
    • Filtering out noise. The vast majority of insider transactions are option exercises, RSU vesting, and 10b5-1 plan sales — none of which signal much. This tool isolates the intentional purchases.
    • Speed. EDGAR filings appear within 24-48 hours of the transaction. For cluster detection (which builds over days or weeks), that latency doesn’t matter.

    What doesn’t work:

    • Single insider buys. One director buying $100K of stock might mean something, but the signal-to-noise ratio is low. Clusters are where the edge is.
    • Short-term trading. This isn’t a day-trading signal. The academic alpha shows up over 3-6 months.
    • Small caps with thin insider data. Some micro-caps only have 2-3 insiders total, so “cluster” detection becomes meaningless.

    Comparing Free Alternatives

    You don’t have to build your own. Here’s how the DIY approach stacks up:

    secform4.com — Free, decent UI, but no cluster detection. You see raw filings, not patterns. No API.

    Finnhub insider endpoint — Free tier includes /stock/insider-transactions, but limited to 100 transactions per call and 60 API calls/minute. Good for single-ticker lookups, not for scanning 500 tickers daily. I wrote about Finnhub and other finance APIs in my finance API comparison.

    OpenInsider.com — My favorite for manual browsing. Has a “cluster buys” filter built in. But no API, no automation, and the cluster definition isn’t configurable.

    The DIY edgartools approach wins if you want customizable filters, automated alerts, and the ability to pipe results into other tools (backtests, portfolio trackers, dashboards). It loses if you just want to glance at insider activity once a week — use OpenInsider for that.

    Running It 24/7 on a Raspberry Pi

    I run my scanner on a Raspberry Pi 5 that also handles a few other Python monitoring scripts. A Pi 5 with 8GB RAM handles this fine — peak memory usage is under 400MB even when scanning all 500 tickers. Total cost: about $80 for the Pi, a case, and an SD card. It’s been running since November without a restart.

    If you’d rather not manage hardware, any $5/month VPS works too. The script runs in about 20 minutes per scan and sleeps the rest of the day.

    Next Steps

    A few things I’m still experimenting with:

    • Combining with technical signals. An insider cluster at a 52-week low with RSI under 30 is more interesting than one at an all-time high. I wrote about RSI and other technical indicators if you want to add that layer.
    • Tracking 13F filings alongside Form 4s. If an insider is buying AND a major fund just initiated a position (visible in quarterly 13F filings), that’s a stronger signal. edgartools handles 13F parsing too.
    • Sector-level clustering. Sometimes multiple insiders across different companies in the same sector all start buying. That’s a sector-level signal I haven’t automated yet.

    If you want to go deeper into the quantitative side, Python for Finance by Yves Hilpisch (O’Reilly) covers the data pipeline and analysis patterns well. Full disclosure: affiliate link.

    The full source code for my detector is about 200 lines. Everything above is production-ready — I copy-pasted from my actual codebase. If you build something with it, I’d be curious to hear what you find.

    For daily market signals and insider activity alerts, join Alpha Signal on Telegram — free market intelligence, no paywall for the daily brief.

  • I Built a Regex Tester Because Regex101 Sends Your Data

    Last week I was debugging a CloudFront log parser and pasted a chunk of raw access logs into Regex101. Mid-keystroke, I realized those logs contained client IPs, user agents, and request paths from production. All of it, shipped off to someone else’s server for “processing.”

    That’s the moment I decided to build my own regex tester.

    The Problem with Existing Regex Testers

    I looked at three tools I’ve used for years:

    Regex101 is the gold standard. Pattern explanations, debugger, community library — it’s feature-rich. But it sends every keystroke to their backend. Their privacy policy says they store patterns and test strings. If you’re testing regex against production data, config files, or anything containing tokens and IPs, that’s a problem.

    RegExr has a solid educational angle with the animated railroad diagrams. But the interface feels like 2015, and there’s no way to test multiple strings against the same pattern without copy-pasting repeatedly.

    Various Chrome extensions promise offline regex testing, but they request permissions to read all your browser data. I’m not trading one privacy concern for a worse one.

    What none of them do: let you define a set of test cases (this string SHOULD match, this one SHOULDN’T) and run them all at once. If you write regex for input validation, URL routing, or log parsing, you need exactly that.

    What I Built

    RegexLab is a single HTML file. No build step, no npm install, no backend. Open it in a browser and it works — including offline, since it registers a service worker.

    Three modes:

    Match mode highlights every match in real-time as you type. Capture groups show up color-coded below the result. If your pattern has named groups or numbered captures, you see exactly what each group caught.

    Replace mode gives you a live preview of string replacement. Type your replacement pattern (with $1, $2 backreferences) and see the output update instantly. I use this constantly for log reformatting and sed-style transforms.

    Multi-test mode is the feature I actually wanted. Add as many test cases as you need. Mark each one as “should match” or “should not match.” Run them all against your pattern and get a pass/fail report. Green checkmark or red X, instantly.

    This is what makes RegexLab different from Regex101. When I’m writing a URL validation pattern, I want to throw 15 different URLs at it — valid ones, edge cases with ports and fragments, obviously invalid ones — and see them all pass or fail in one view. No scrolling, no re-running.

    How It Works Under the Hood

    The entire app is ~30KB of HTML, CSS, and JavaScript. No frameworks, no dependencies. Here’s what’s happening technically:

    Pattern compilation: Every keystroke triggers a debounced (80ms) recompile. The regex is compiled with new RegExp(pattern, flags) inside a try/catch. Invalid patterns show the error message directly — no cryptic “SyntaxError,” just the relevant part of the browser’s error string.

    Match highlighting: I use RegExp.exec() in a loop with the global flag to find every match with its index position. Then I build highlighted HTML by slicing the original string at match boundaries and wrapping matches in <span class="hl"> tags. A safety counter at 10,000 prevents infinite loops from zero-length matches (a real hazard with patterns like .*).

    // Simplified version of the match loop
    const rxCopy = new RegExp(rx.source, rx.flags);
    let safety = 0;
    while ((m = rxCopy.exec(text)) !== null && safety < 10000) {
      matches.push({ start: m.index, end: m.index + m[0].length });
      if (m[0].length === 0) rxCopy.lastIndex++;
      safety++;
    }

    That lastIndex++ on zero-length matches is important. Without it, a pattern like /a*/g will match the empty string forever at the same position. Every regex tutorial skips this, and then people wonder why their browser tab freezes.

    Capture groups: When exec() returns an array with more than one element, elements at index 1+ are capture groups. I color-code them with four rotating colors (amber, pink, cyan, purple) and display them below the match result.

    Flag toggles: The flag buttons sync bidirectionally with the text input. Click a button, the text field updates. Type in the text field, the buttons update. I store flags as a simple string ("gim") and reconstruct it from button state on each click.

    State persistence: Everything saves to localStorage every 2 seconds — pattern, flags, test string, replacement, and all test cases. Reload the page and you’re right where you left off. The service worker caches the HTML for offline use.

    Common patterns library: 25 pre-built patterns for emails, URLs, IPs, dates, UUIDs, credit cards, semantic versions, and more. Click one to load it. Searchable. I pulled these from my own .bashrc aliases and validation functions I’ve written over the years.

    Design Decisions

    Dark mode by default via prefers-color-scheme. Most developers use dark themes. The light mode is there for the four people who don’t.

    Monospace everywhere that matters. Pattern input, test strings, results — all in SF Mono / Cascadia Code / Fira Code. Proportional fonts in regex testing are a war crime.

    No syntax highlighting in the pattern input. I considered it, but colored brackets and escaped characters inside an input field add complexity without much benefit. The error message and match highlighting already tell you if your pattern is right.

    Touch targets are 44px minimum. The flag toggle buttons, tab buttons, and action buttons all meet Apple’s HIG recommendation. I tested at 320px viewport width on my phone and everything still works.

    Real Use Cases

    Log parsing: I parse nginx access logs daily. A pattern like (\d+\.\d+\.\d+\.\d+).*?"(GET|POST)\s+([^"]+)"\s+(\d{3}) pulls IP, method, path, and status code. Multi-test mode lets me throw 10 sample log lines at it to make sure edge cases (HTTP/2 requests, URLs with quotes) don’t break it.

    Input validation: Building a form? Test your email/phone/date regex against a list of valid and invalid inputs in one shot. Way faster than manually testing each one.

    Search and replace: Reformatting dates from MM/DD/YYYY to YYYY-MM-DD? The replace mode with $3-$1-$2 backreferences shows you the result instantly.

    Teaching: The pattern library doubles as a learning resource. Click “Email” or “UUID” and see a production-quality regex with its flags and description. Better than Stack Overflow answers from 2012.

    Try It

    It’s live at regexlab.orthogonal.info. Works offline after the first visit. Install it as a PWA if you want it in your dock.

    If you want more tools like this — HashForge for hashing, JSON Forge for formatting JSON, QuickShrink for image compression — they’re all at apps.orthogonal.info. Same principle: single HTML file, zero dependencies, your data stays in your browser.

    Full disclosure: Mastering Regular Expressions by Jeffrey Friedl is the book that made regex click for me back in college. If you’re still guessing at lookaheads and backreferences, it’s worth the read. Regular Expressions Cookbook by Goyvaerts and Levithan is also solid if you want recipes rather than theory. And if you’re doing a lot of text processing, a good mechanical keyboard makes the difference when you’re typing backslashes all day.

    More Privacy-First Browser Tools

  • Docker Compose vs Kubernetes: Secure Homelab Choices

    Docker Compose vs Kubernetes: Secure Homelab Choices

    Last year I moved my homelab from a single Docker Compose stack to a K3s cluster. It took a weekend, broke half my services, and taught me more about container security than any course I’ve taken. Here’s what I learned about when each tool actually makes sense—and the security traps in both.

    The real question: how big is your homelab?

    I ran Docker Compose for two years. Password manager, Jellyfin, Gitea, a reverse proxy, some monitoring. Maybe 12 containers. It worked fine. The YAML was readable, docker compose up -d got everything running in seconds, and I could debug problems by reading one file.

    Then I hit ~25 containers across three machines. Compose started showing cracks—no built-in way to schedule across nodes, no health-based restarts that actually worked reliably, and secrets management was basically “put it in an .env file and hope nobody reads it.”

    That’s when I looked at Kubernetes seriously. Not because it’s trendy, but because I needed workload isolation, proper RBAC, and network policies that Docker’s bridge networking couldn’t give me.

    Docker Compose security: what most people miss

    Compose is great for getting started, but it has security defaults that will bite you. The biggest one: containers run as root by default. Most people never change this.

    Here’s the minimum I run on every Compose service now:

    version: '3.8'
    services:
      app:
        image: my-app:latest
        user: "1000:1000"
        read_only: true
        security_opt:
          - no-new-privileges:true
        cap_drop:
          - ALL
        deploy:
          resources:
            limits:
              memory: 512M
              cpus: '0.5'
        networks:
          - isolated
        logging:
          driver: json-file
          options:
            max-size: "10m"
    
    networks:
      isolated:
        driver: bridge
    

    The key additions most tutorials skip: read_only: true prevents containers from writing to their filesystem (mount specific writable paths if needed), no-new-privileges blocks privilege escalation, and cap_drop: ALL removes Linux capabilities you almost certainly don’t need.

    Other things I do with Compose that aren’t optional anymore:

    • Network segmentation. Separate Docker networks for databases, frontend services, and monitoring. My Postgres container can’t talk to Traefik directly—it goes through the app layer only.
    • Image scanning. I run Trivy on every image before deploying. One trivy image my-app:latest catches CVEs that would otherwise sit there for months.
    • TLS everywhere. Even internal services get certificates via Let’s Encrypt and Traefik’s ACME resolver.

    Scan your images before they run—it takes 10 seconds and catches the obvious stuff:

    # Quick scan
    trivy image my-app:latest
    
    # Fail CI if HIGH/CRITICAL vulns found
    trivy image --exit-code 1 --severity HIGH,CRITICAL my-app:latest
    

    Kubernetes: when the complexity pays off

    I use K3s specifically because full Kubernetes is absurd for a homelab. K3s strips out the cloud-provider bloat and runs the control plane in a single binary. My cluster runs on a TrueNAS box with 32GB RAM—plenty for ~40 pods.

    The security features that actually matter for homelabs:

    RBAC — I can give my partner read-only access to monitoring dashboards without exposing cluster admin. Here’s a minimal read-only role:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      namespace: monitoring
      name: dashboard-viewer
    rules:
    - apiGroups: [""]
      resources: ["pods", "services"]
      verbs: ["get", "list", "watch"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: viewer-binding
      namespace: monitoring
    subjects:
    - kind: User
      name: reader
      apiGroup: rbac.authorization.k8s.io
    roleRef:
      kind: Role
      name: dashboard-viewer
      apiGroup: rbac.authorization.k8s.io
    

    Network policies — This is the killer feature. In Compose, network isolation is coarse (whole networks). In Kubernetes, I can say “this pod can only talk to that pod on port 5432, nothing else.” If a container gets compromised, lateral movement is blocked.

    Namespaces — I run separate namespaces for media, security tools, monitoring, and databases. Each namespace has its own resource quotas and network policies. A runaway Jellyfin transcode can’t starve my password manager.

    The tradeoff is real though. I spent a full day debugging a network policy that was silently dropping traffic between my app and its database. The YAML looked right. Turned out I had a label mismatch—app: postgres vs app: postgresql. Kubernetes won’t warn you about this. It just drops packets.

    Networking: the part everyone gets wrong

    Whether you’re on Compose or Kubernetes, your reverse proxy config matters more than most security settings. I use Traefik for both setups. Here’s my Compose config for automatic TLS:

    version: '3.8'
    services:
      traefik:
        image: traefik:v3.0
        command:
          - "--entrypoints.web.address=:80"
          - "--entrypoints.websecure.address=:443"
          - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
          - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
          - "[email protected]"
          - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
        volumes:
          - "./letsencrypt:/letsencrypt"
        ports:
          - "80:80"
          - "443:443"
    

    Key detail: that HTTP-to-HTTPS redirect on the web entrypoint. Without it, you’ll have services accessible over plain HTTP and not realize it until someone sniffs your traffic.

    For storage, encrypt volumes at rest. If you’re on ZFS (like my TrueNAS setup), native encryption handles this. For Docker volumes specifically:

    # Create a volume backed by encrypted storage
    docker volume create --driver local \
      --opt type=none \
      --opt o=bind \
      --opt device=/mnt/encrypted/app-data \
      my_secure_volume
    

    My recommendation: start Compose, graduate to K3s

    If you have fewer than 15 containers on one machine, stick with Docker Compose. Apply the security hardening above, scan your images, segment your networks. You’ll be fine.

    Once you hit multiple nodes, need proper secrets management (not .env files), or want network-policy-level isolation, move to K3s. Not full Kubernetes—K3s. The learning curve is steep for a week, then it clicks.

    I’d also recommend adding Falco for runtime monitoring regardless of which tool you pick. It watches syscalls and alerts on suspicious behavior—like a container suddenly spawning a shell or reading /etc/shadow. Worth the 5 minutes to set up.

    The tools I keep coming back to for this:

    Related posts you might find useful:

    Get daily AI-powered market intelligence. Join Alpha Signal — free market briefs, security alerts, and dev tool recommendations.
    📦 Disclosure: Some links above are affiliate links. If you buy through them, I earn a small commission at no extra cost to you. I only recommend stuff I actually use. This helps keep orthogonal.info running.
  • Best Drives for TrueNAS in 2026: HDDs, SSDs, and What I Actually Run

    Last month I lost a drive in my TrueNAS mirror. WD Red, three years old, SMART warnings I’d been ignoring for two weeks. The rebuild took 14 hours on spinning rust, and the whole time I was thinking: if the second drive goes, that’s 8TB of media and backups gone.

    That rebuild forced me to actually research what I was putting in my NAS instead of just grabbing whatever was on sale. Turns out, picking the right drives for ZFS matters more than most people realize — and the wrong choice can cost you data or performance.

    Here’s what I learned, what I’m running now, and what I’d buy if I were building from scratch today.

    CMR vs. SMR: This Actually Matters for ZFS

    Before anything else — check if your drive uses CMR (Conventional Magnetic Recording) or SMR (Shingled Magnetic Recording). ZFS and SMR don’t get along. SMR drives use overlapping write tracks to squeeze in more capacity, which means random writes are painfully slow. During a resilver (ZFS’s version of a rebuild), an SMR drive can take 3-4x longer than CMR.

    WD got caught shipping SMR drives labeled as NAS drives back in 2020 (the WD Red debacle). They’ve since split the line: WD Red Plus = CMR, plain WD Red = SMR. Don’t buy the plain WD Red for a NAS. I made this mistake once. Never again.

    Seagate’s IronWolf line is all CMR. Toshiba N300 — also CMR. If you’re looking at used enterprise drives (which I’ll get to), they’re all CMR.

    The Drives I’d Actually Buy Today

    For Bulk Storage: WD Red Plus 8TB

    The WD Red Plus 8TB (WD80EFPX) is what I’m running right now. 5640 RPM, CMR, 256MB cache. It’s not the fastest drive, but it runs cool and quiet — important when your NAS sits in a closet six feet from your bedroom.

    Price per TB on the 8TB sits around $15-17 at time of writing. The sweet spot for capacity vs. cost. Going to 12TB or 16TB drops the per-TB price slightly, but the failure risk per drive goes up — a single 16TB drive failing is a lot more data at risk during rebuild than an 8TB.

    I run these in a mirror (RAID1 equivalent in ZFS). Two drives, same data on both. Simple, reliable, and rebuild time is reasonable. Full disclosure: affiliate link.

    If You Prefer Seagate: IronWolf 8TB

    The Seagate IronWolf 8TB (ST8000VN004) is the other solid choice. 7200 RPM, CMR, 256MB cache. Faster spindle speed means slightly better sequential performance, but also more heat and noise.

    Seagate includes their IronWolf Health Management software, which hooks into most NAS operating systems including TrueNAS. It gives you better drive health telemetry than standard SMART. Whether that’s worth the slightly higher price depends on how paranoid you are about early failure detection. (I’m very paranoid, but I still went WD — old habits.)

    Both drives have a 3-year warranty. The IronWolf Pro bumps that to 5 years and adds rotational vibration sensors (matters more in 8+ bay enclosures). For a 4-bay homelab NAS, the standard IronWolf is enough. Full disclosure: affiliate link.

    Budget Option: Used Enterprise Drives

    Here’s my hot take: refurbished enterprise drives are underrated for homelabs. An HGST Ultrastar HC320 8TB can be found for $60-80 on eBay — roughly half the price of new consumer NAS drives. These were built for 24/7 operation in data centers. They’re louder (full 7200 RPM, no acoustic management), but they’re tanks.

    The catch: no warranty, unknown hours, and you’re gambling on remaining lifespan. I run one pool with used enterprise drives and another with new WD Reds. The enterprise drives have been fine for two years. But I also keep backups, because I’m not an idiot.

    SSDs in TrueNAS: SLOG, L2ARC, and When They’re Worth It

    ZFS has two SSD acceleration features that confuse a lot of people: SLOG (write cache) and L2ARC (read cache). Let me save you some research time.

    SLOG (Separate Log Device)

    SLOG moves the ZFS Intent Log to a dedicated SSD. This only helps if you’re doing a lot of synchronous writes — think iSCSI targets, NFS with sync enabled, or databases. If you’re mostly streaming media and storing backups, SLOG does nothing for you.

    If you DO need a SLOG, the drive needs high write endurance and a power-loss protection capacitor. The Intel Optane P1600X 118GB is the gold standard here — extremely low latency and designed for exactly this workload. They’re getting harder to find since Intel killed the Optane line, but they pop up on Amazon periodically. Full disclosure: affiliate link.

    Don’t use a consumer NVMe SSD as a SLOG. If it loses power mid-write without a capacitor, you can lose the entire transaction log. That’s your data.

    L2ARC (Level 2 Adaptive Replacement Cache)

    L2ARC is a read cache on SSD that extends your ARC (which lives in RAM). The thing most guides don’t tell you: L2ARC uses about 50-70 bytes of RAM per cached block to maintain its index. So adding a 1TB L2ARC SSD might eat 5-10GB of RAM just for the metadata.

    Rule of thumb: if you have less than 64GB of RAM, L2ARC probably hurts more than it helps. Your RAM IS your cache in ZFS — spend money on more RAM before adding an L2ARC SSD. I learned this the hard way on a 32GB system where L2ARC actually slowed things down.

    If you do have the RAM headroom and want L2ARC, any decent NVMe drive works. I’d grab a Samsung 990 EVO 1TB — good endurance, solid random read performance, and the price has come down a lot. Full disclosure: affiliate link.

    What My Actual Setup Looks Like

    For context, I run TrueNAS Scale on an older Xeon workstation with 64GB ECC RAM. Here’s the drive layout:

    Pool: tank (main storage)
      Mirror: 2x WD Red Plus 8TB (WD80EFPX)
      
    Pool: fast (VMs and containers) 
      Mirror: 2x Samsung 870 EVO 1TB SATA SSD
    
    No SLOG (my workloads are async)
    No L2ARC (64GB RAM handles my working set)

    Total usable: ~8TB spinning + ~1TB SSD. The SSD pool runs my Docker containers and any VM images. Everything else — media, backups, time machine targets — lives on the spinning rust pool.

    This separation matters. ZFS performs best when pools have consistent drive types. Mixing SSDs and HDDs in the same vdev is asking for trouble (the pool performs at the speed of the slowest drive).

    ECC RAM: Not Optional, Fight Me

    While we’re talking about TrueNAS hardware — get ECC RAM. Yes, TrueNAS will run without it. No, that doesn’t mean you should.

    ZFS checksums every block, which means it can detect corruption. But if your RAM flips a bit (which non-ECC RAM does more often than you’d think), ZFS might write that corrupted data to disk AND update the checksum to match. Now you have silent data corruption that ZFS thinks is fine. With ECC, the memory controller catches and corrects single-bit errors before they hit disk.

    Used DDR4 ECC UDIMMs are cheap. A 32GB kit runs $40-60 on eBay. There’s no excuse not to use it if your board supports it. If you’re building a new system, look at Xeon E-series or AMD platforms that support ECC.

    How to Check What You Already Have

    Already running TrueNAS? Here’s how to check your drive health before something fails:

    # List all drives with model and serial
    smartctl --scan | while read dev rest; do
      echo "=== $dev ==="
      smartctl -i "$dev" | grep -E "Model|Serial|Capacity"
      smartctl -A "$dev" | grep -E "Reallocated|Current_Pending|Power_On"
    done
    
    # Quick ZFS pool health check
    zpool status -v

    Watch for Reallocated_Sector_Ct above zero and Current_Pending_Sector above zero. Those are your early warning signs. If both are climbing, start shopping for a replacement drive now — don’t wait for the failure like I did.

    The Short Version

    If you’re building a TrueNAS box in 2026:

    • Bulk storage: WD Red Plus or Seagate IronWolf. CMR only. 8TB is the sweet spot for price per TB.
    • SLOG: Only if you need sync writes. Intel Optane if you can find one. Otherwise skip it.
    • L2ARC: Only if you have 64GB+ RAM to spare. Any NVMe SSD works.
    • RAM: ECC or go home. At least 1GB per TB of storage, 32GB minimum.
    • Budget move: Used enterprise HDDs + new SSDs for VM pool. Loud but reliable.

    Don’t overthink it. Get CMR drives, get ECC RAM, keep backups. Everything else is optimization.

    If you found this useful, check out my guides on self-hosting Ollama on your homelab, backup and recovery for homelabs, and setting up Wazuh and Suricata for home security monitoring.


    Join https://t.me/alphasignal822 for free market intelligence.

  • Claude Code Leak: Lessons in npm Security, TypeScript Analysis, and AI Tool Architecture

    Claude Code Leak: Lessons in npm Security, TypeScript Analysis, and AI Tool Architecture

    The error made no sense: “Cannot find module ‘./dist/index.js’. Please verify that the package.json has a valid ‘main’ entry.” Again. You double-checked the file path. You triple-checked the file path. You even sacrificed your lunch break to manually inspect the node_modules folder like a digital archaeologist. Still, nothing. What was supposed to be a quick npm install has turned into a full-blown existential crisis. Is it you? Is it the package? Or is it… something deeper?

    If you’ve ever been blindsided by a rogue npm package or spent hours unraveling the mysteries of a TypeScript codebase that feels more like a labyrinth than a project, you’re not alone. In this article, we’ll dive into the importance of npm security, dissect the risks associated with leaked source maps, and uncover some architectural insights for AI coding tools. Whether you’re a software engineer trying to keep your CI/CD pipeline from imploding or a DevSecOps pro hunting for vulnerabilities, this one’s for you. And don’t worry—we’ll keep it light, because debugging is already serious enough.


    Introduction: Why npm Security Matters

    Alright, let’s talk about the elephant in the server room: npm security. If you haven’t heard, there have been numerous incidents where sensitive files or configurations found their way into the wild due to improper packaging or publishing practices. Think of it as accidentally leaving your TypeScript codebase out in the rain—except the rain is the entire internet, and your code is now everyone’s business. For software engineers and DevSecOps pros like us, this is a wake-up call to rethink how we handle security, especially when it comes to npm packages and build artifacts.

    Here’s the thing: npm packages are the backbone of modern development. They’re great for sharing reusable code, managing dependencies, and speeding up development workflows. But when security lapses occur—like improperly secured source maps or sensitive files making their way into public repositories—it’s like leaving your house keys under the doormat. Sure, it’s convenient, but it’s also an open invitation to trouble.

    In this article, we’ll dive into the implications of npm security, why protecting source maps is more critical than ever, and how to safeguard your projects from similar mishaps. From analyzing your TypeScript codebase for vulnerabilities to ensuring your source maps don’t spill the beans, we’ve got you covered. And yes, there will be a few jokes along the way—because if we can’t laugh at our mistakes, what’s the point?

    💡 Pro Tip: Always double-check your .gitignore file. It’s like a bouncer for your repo—don’t let sensitive files sneak past it.

    Understanding the Security Implications of Leaked Source Maps

    Ah, source maps. They’re like the treasure maps of your TypeScript codebase—except instead of leading to gold doubloons, they lead to your beautifully crafted code. If you’ve ever debugged a web app, you’ve probably thanked the heavens for source maps. They translate your minified JavaScript back into something readable, making debugging less of a soul-crushing experience. But here’s the catch: when these maps are leaked, they can expose sensitive information faster than I expose my lack of gym attendance.

    Let’s break it down. Source maps are files that link your compiled JavaScript code back to the original TypeScript (or whatever language you’re using). They’re a lifesaver for developers, but they’re also a potential goldmine for attackers. Why? Because they reveal your code structure, variable names, and sometimes even comments—basically everything you’d rather keep private. If you’re shipping npm packages, you need to care about npm source maps security. Otherwise, you might accidentally hand over your app’s blueprint to the bad guys.

    So, what’s the real-world risk here? Attackers can use leaked source maps to reverse-engineer your app, identify vulnerabilities, and exploit them. They can even uncover hidden secrets like API keys or proprietary algorithms. If you’re using AI coding tools, you might be tempted to rely on them for code analysis, but remember: even AI can’t save you from bad security practices.

    💡 Pro Tip: Always double-check your build pipeline to ensure source maps are excluded from production builds. Tools like Webpack and Rollup make this easy—just tweak your config!

    In conclusion, leaked source maps are like leaving the back door open for hackers. They’re a small detail that can have massive consequences. If you’re working with npm packages or AI coding tools, take the time to review your security practices. Trust me, you don’t want to be the next headline in the “DevSecOps Horror Stories” newsletter.

    Analyzing a TypeScript Codebase: Architectural Insights

    Ah, large-scale TypeScript codebases. They’re like opening a treasure chest, except instead of gold coins, you find a labyrinth of TypeScript files, multi-agent orchestration patterns, and enough modular design to make a LEGO engineer weep with envy. Let’s dive into this architectural marvel (or monstrosity, depending on your caffeine levels) and see what makes it tick—and why analyzing it is like trying to untangle a pair of headphones you left in your pocket for a year.

    First off, let’s talk structure. A well-designed TypeScript codebase is often divided into neatly organized modules, each with its own specific role, but they all come together in a way that feels like a symphony—if the conductor were a robot trying to learn Beethoven on the fly.

    One of the standout architectural patterns here is multi-agent orchestration. Think of it as a group of highly specialized agents (or microservices) working together to accomplish tasks. Each agent knows its job, communicates with others through well-defined APIs, and occasionally throws an error just to remind you it’s still a machine. This pattern is great for scalability and fault isolation, but it can also make debugging feel like herding cats—except the cats are on different continents and speak different programming languages.

    Another key feature is the codebase’s modular design. Modules are like LEGO bricks: self-contained, reusable, and capable of building something amazing when combined. But here’s the catch—just like LEGO, if you step on the wrong module (read: introduce a breaking change), it’s going to hurt. A lot. The modularity is a double-edged sword: it keeps the codebase maintainable but also means you need a map, a flashlight, and probably a snack to navigate it effectively.

    💡 Pro Tip: When analyzing large-scale TypeScript projects, always start with the package.json. It’s the Rosetta Stone of dependencies and can save you hours of head-scratching.

    Now, let’s address the elephant in the room: analyzing large-scale TypeScript projects is hard. Between the sheer volume of files, the intricate dependency graphs, and the occasional “What were they thinking?” moments, it’s easy to feel overwhelmed. And don’t get me started on npm source maps security. If you’re not careful, those source maps can leak sensitive information faster than you can say “security breach.”

    Here’s a quick example of what you might encounter in a codebase like this:

    
    // A simple example of a modular agent in TypeScript
    export class DataProcessor {
        private data: string[];
    
        constructor(data: string[]) {
            this.data = data;
        }
    
        public process(): string[] {
            return this.data.map(item => item.toUpperCase());
        }
    }
    
    // Usage
    const processor = new DataProcessor(['typescript', 'codebase', 'analysis']);
    console.log(processor.process()); // Output: ['TYPESCRIPT', 'CODEBASE', 'ANALYSIS']
    

    Looks straightforward, right? Now imagine this times a thousand, with interdependencies, async calls, and a sprinkle of AI magic. Welcome to the world of TypeScript codebase analysis!

    In conclusion, analyzing a large-scale TypeScript codebase is a testament to the power and complexity of modern software engineering. It’s a fascinating mix of brilliance and frustration, much like trying to assemble IKEA furniture without the instructions. If you’re diving into a project like this, remember to take it one module at a time, keep an eye on those source maps, and don’t forget to laugh at the absurdity of it all. After all, if you’re not having fun, what’s the point?

    Best Practices for npm Security: Lessons Learned

    Let’s dive into how you can prevent accidental exposure of sensitive files during npm publishes, use tools like .npmignore and package.json configurations effectively, and leverage automated tools and CI/CD pipelines for npm security. Spoiler alert: it’s easier than you think, but you’ll need to pay attention to the details.

    Step 1: Don’t Let npm Publish Your Dirty Laundry

    First things first: understand what gets published when you run npm publish. By default, npm will include everything in your project directory unless you explicitly tell it not to. This means those juicy .env files, debug logs, or even your secret stash of cat memes could end up on the internet. To avoid this, you need to curate what goes into your package like a chef picking only the freshest ingredients.

    💡 Pro Tip: Use npm pack to preview what will be included in your package before publishing. Think of it as a dress rehearsal for your code.

    Step 2: Mastering .npmignore and package.json

    The .npmignore file is your best friend here. It works like .gitignore, but for npm. You can specify files and directories to exclude from your package. For example:

    
    # .npmignore
    .env
    debug.log
    node_modules
    tests/
    

    If you’re already using .gitignore, npm will use it by default unless you provide a .npmignore. But be careful—just because it’s ignored by Git doesn’t mean it’s safe from npm. Always double-check!

    Another handy trick is to use the files field in your package.json. This lets you explicitly list what should be included in your package, like so:

    
    {
      "files": [
        "dist/",
        "src/",
        "README.md"
      ]
    }
    

    Think of this as the VIP guest list for your npm package. If it’s not on the list, it’s not getting in.

    Step 3: Automate Like Your Life Depends on It

    Let’s be real: humans are terrible at repetitive tasks. That’s why automated tools and CI/CD pipelines are a godsend for npm security. You can use tools like npm-check or audit-ci to scan for vulnerabilities before publishing. Combine these with a CI/CD pipeline that runs tests, checks for sensitive files, and ensures your package is production-ready.

    Here’s an example of a simple CI/CD pipeline step using GitHub Actions:

    
    # .github/workflows/npm-publish.yml
    name: Publish Package
    
    on:
      push:
        branches:
          - main
    
    jobs:
      publish:
        runs-on: ubuntu-latest
        steps:
          - name: Checkout code
            uses: actions/checkout@v3
          - name: Install dependencies
            run: npm install
          - name: Run security checks
            run: npm audit
          - name: Publish package
            run: npm publish --access public
    

    With this setup, you can sleep soundly knowing your pipeline is doing the heavy lifting for you. Just make sure your tests are thorough—don’t be that person who skips the hard stuff.

    Final Thoughts

    npm security isn’t something to take lightly. By using .npmignore, package.json configurations, and automated tools, you can avoid accidental exposure of sensitive files and keep your codebase safe. Trust me, your future self will thank you.

    And hey, if you ever feel overwhelmed, just remember: even the best of us have accidentally published our “middle school poetry” at some point. The key is to learn, adapt, and automate like your career depends on it—because it probably does.

    Actionable Takeaways for DevSecOps Professionals

    Let’s face it: DevSecOps is like trying to juggle flaming swords while riding a unicycle. You’re balancing speed, security, and sanity, and sometimes it feels like you’re one missed patch away from a headline-grabbing security breach. But don’t worry—here are some actionable tips to keep your codebase secure without losing your mind (or your job).

    First up, integrating security checks into your development lifecycle. Think of it like flossing your teeth—annoying, but skipping it will come back to bite you. Tools like static code analyzers and dependency scanners should be baked into your CI/CD pipeline. If you’re working with a TypeScript codebase, for example, make sure you’re running automated checks for vulnerabilities in your npm packages. Trust me, you don’t want to find out about a security flaw after your app is live. That’s like realizing your parachute has a hole after you’ve jumped out of the plane.

    Next, let’s talk about npm source maps security. Source maps are great for debugging, but leaving them exposed in production is like leaving your house key under the doormat with a neon sign pointing to it. Educate your team about the risks and make sure source maps are stripped out before deployment. If you’re not sure how to do this, don’t worry—you’re not alone. I once accidentally deployed a source map that revealed our entire API structure. My team still teases me about it.

    Finally, proactive monitoring for exposed files in public repositories. This one’s a no-brainer. Use tools to scan your repos for sensitive files like .env or private keys. Think of it as running a metal detector over your beach of code—you never know what treasures (or landmines) you’ll find. And if you do find something embarrassing, fix it fast and pretend it never happened. That’s what I do with my old blog posts.

    💡 Pro Tip: Automate everything you can. The less manual work, the fewer chances for human error. Plus, it gives you more time to binge-watch cat videos.

    In summary, DevSecOps isn’t about being perfect—it’s about being prepared. Secure your pipeline, educate your team, and monitor like your job depends on it. Because, well, it probably does.

    🛠️ Recommended Resources:

    Tools and books mentioned in (or relevant to) this article:

    Conclusion: Building Secure and Scalable AI Coding Tools

    So, what did we learn? Well, for starters, even the most advanced tools can trip over their own shoelaces if security isn’t baked into their design. It’s like building a rocket ship but forgetting to lock the door—impressive, but also terrifying. Security is a wake-up call for anyone working with npm packages and AI coding tools.

    One key takeaway is the importance of balancing innovation with security. Sure, pushing boundaries is fun—who doesn’t love a shiny new TypeScript feature? But if you’re not securing your npm source maps, you’re basically leaving breadcrumbs for attackers to follow. And trust me, they will.

    💡 Pro Tip: Regularly audit your TypeScript codebase and lock down your npm dependencies. Future-you will thank you.

    So here’s my call to action: adopt best practices in npm security. Use tools, automate checks, and don’t treat security as an afterthought. Let’s build tools that are not just smart, but also safe. Deal?

    📋 Disclosure: Some links in this article are affiliate links. If you purchase through these links, I earn a small commission at no extra cost to you. I only recommend products I’ve personally used or thoroughly evaluated. This helps support orthogonal.info and keeps the content free.

    Related Security Reading