<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Max</title>
    <description>The latest articles on DEV Community by Max (@orthogonalinfo).</description>
    <link>https://dev.to/orthogonalinfo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3847175%2F78878eb1-022c-4880-ba72-cde851bc87d8.png</url>
      <title>DEV Community: Max</title>
      <link>https://dev.to/orthogonalinfo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/orthogonalinfo"/>
    <language>en</language>
    <item>
      <title>TinyPNG vs QuickShrink: Why I Switched to Client-Side Image Compression</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Wed, 27 May 2026 15:03:51 +0000</pubDate>
      <link>https://dev.to/orthogonalinfo/tinypng-vs-quickshrink-why-i-switched-to-client-side-image-compression-56b9</link>
      <guid>https://dev.to/orthogonalinfo/tinypng-vs-quickshrink-why-i-switched-to-client-side-image-compression-56b9</guid>
      <description>&lt;p&gt;Every web developer knows TinyPNG. It's been the go-to image compressor for years. But after years of using it, I started questioning: &lt;strong&gt;why am I uploading my images to someone else's server?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Server-Side Compression
&lt;/h2&gt;

&lt;p&gt;Tools like TinyPNG, Compressor.io, and even Squoosh (partially) require you to upload images to their servers. For most casual use, that's fine. But consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NDA projects&lt;/strong&gt; — client mockups, unreleased product photos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Medical/legal images&lt;/strong&gt; — patient data, case evidence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal photos&lt;/strong&gt; — family photos you want smaller but private&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise work&lt;/strong&gt; — screenshots of internal dashboards, Slack messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every upload is a data transfer you can't take back.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Client-Side Compression?
&lt;/h2&gt;

&lt;p&gt;Instead of sending your image to a server for processing, client-side compression uses your browser's built-in Canvas API and WebP encoding to compress images &lt;strong&gt;locally&lt;/strong&gt;. The file never leaves your device.&lt;/p&gt;

&lt;p&gt;Open DevTools → Network tab → compress an image → &lt;strong&gt;zero outgoing requests&lt;/strong&gt;. That's the proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison: TinyPNG vs QuickShrink
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;TinyPNG&lt;/th&gt;
&lt;th&gt;QuickShrink&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Privacy&lt;/td&gt;
&lt;td&gt;Server-side (images uploaded)&lt;/td&gt;
&lt;td&gt;100% client-side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch&lt;/td&gt;
&lt;td&gt;Yes (20 images, 5MB each)&lt;/td&gt;
&lt;td&gt;Yes (unlimited size)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebP output&lt;/td&gt;
&lt;td&gt;No (PNG/JPEG only)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resize&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quality presets&lt;/td&gt;
&lt;td&gt;No (auto only)&lt;/td&gt;
&lt;td&gt;Web, Social, Email, Print&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offline&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (PWA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;Yes ($)&lt;/td&gt;
&lt;td&gt;Coming soon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price&lt;/td&gt;
&lt;td&gt;Free (20/day) or 9/yr&lt;/td&gt;
&lt;td&gt;Free (5/day) or .99/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Compression Quality
&lt;/h2&gt;

&lt;p&gt;Let's be honest — TinyPNG's lossy PNG compression is excellent. Their algorithm (quantization + DEFLATE) produces incredibly small PNGs with minimal quality loss.&lt;/p&gt;

&lt;p&gt;QuickShrink uses Canvas API resampling + WebP encoding, which takes a different approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JPEG → WebP&lt;/strong&gt;: typically 40-60% smaller at equivalent quality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PNG → WebP&lt;/strong&gt;: typically 50-80% smaller (lossy WebP vs lossless PNG)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JPEG → JPEG&lt;/strong&gt;: 20-40% smaller with quality adjustment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tradeoff: TinyPNG gives better PNG-to-PNG results. QuickShrink wins when you convert to WebP (which you should for web use in 2024+).&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Which
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use TinyPNG when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need PNG output specifically&lt;/li&gt;
&lt;li&gt;Images aren't sensitive&lt;/li&gt;
&lt;li&gt;You want the absolute smallest PNG&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use QuickShrink when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Privacy matters (NDAs, sensitive content)&lt;/li&gt;
&lt;li&gt;You want WebP output (better web performance)&lt;/li&gt;
&lt;li&gt;You need resize + compress in one step&lt;/li&gt;
&lt;li&gt;You want offline capability&lt;/li&gt;
&lt;li&gt;You don't want to create an account&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://quickshrink.orthogonal.info" rel="noopener noreferrer"&gt;QuickShrink&lt;/a&gt; — free, no signup, works in any modern browser.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your image compression workflow? Still uploading to servers, or have you gone client-side?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>privacy</category>
      <category>tools</category>
    </item>
    <item>
      <title>I Caught 14 Leaked Secrets in My Git History — Here is the Pre-Commit Setup That Stops It</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Tue, 26 May 2026 17:03:43 +0000</pubDate>
      <link>https://dev.to/orthogonalinfo/i-caught-14-leaked-secrets-in-my-git-history-here-is-the-pre-commit-setup-that-stops-it-df4</link>
      <guid>https://dev.to/orthogonalinfo/i-caught-14-leaked-secrets-in-my-git-history-here-is-the-pre-commit-setup-that-stops-it-df4</guid>
      <description>&lt;p&gt;Last month I ran &lt;code&gt;trufflehog&lt;/code&gt; against one of my private repos — a homelab automation project I’d never planned to open-source. It found 14 live secrets. AWS keys, a Telegram bot token, two database passwords, and a Stripe test key that still had access to customer data. All committed between 2022 and 2024, scattered across dozens of commits.&lt;/p&gt;

&lt;p&gt;The fix took me about 20 minutes. I now run two tools as pre-commit hooks that catch secrets before they ever reach &lt;code&gt;.git/objects&lt;/code&gt;. Here’s exactly how I set it up, what each tool catches that the other misses, and the one configuration mistake that will give you false confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Two Tools: git-secrets vs trufflehog
&lt;/h2&gt;

&lt;p&gt;I use both &lt;a href="https://github.com/awslabs/git-secrets" rel="noopener noreferrer"&gt;git-secrets&lt;/a&gt; and &lt;a href="https://github.com/trufflesecurity/trufflehog" rel="noopener noreferrer"&gt;trufflehog&lt;/a&gt; because they work differently and catch different things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;git-secrets&lt;/strong&gt; is pattern-based. It ships with AWS-specific patterns out of the box (matches &lt;code&gt;AKIA[0-9A-Z]{16}&lt;/code&gt; and similar) and lets you add custom regexes. It’s fast — sub-100ms on most commits — and runs as a native git hook. The downside: it only knows what you tell it to look for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;trufflehog&lt;/strong&gt; uses entropy detection &lt;em&gt;and&lt;/em&gt; pattern matching. It calculates Shannon entropy on strings and flags anything that looks random enough to be a key. Version 3 also verifies secrets against live APIs — it’ll actually try your AWS key against STS to confirm it’s active. This is slower (2-5 seconds per commit) but catches novel secret formats that pattern matching misses.&lt;/p&gt;

&lt;p&gt;In my 14-secret audit, git-secrets would have caught 9 of them. trufflehog caught all 14. But git-secrets has zero false positives in my workflow, while trufflehog flags about 1 false positive per week on base64-encoded config blobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up git-secrets as a Pre-Commit Hook
&lt;/h2&gt;

&lt;p&gt;Install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install git-secrets   # macOS
# or
git clone https://github.com/awslabs/git-secrets.git
cd git-secrets &amp;amp;&amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register it in your repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd your-repo
git secrets --install
git secrets --register-aws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;--register-aws&lt;/code&gt; flag adds patterns for AWS access keys, secret keys, and account IDs. Now add your own patterns for whatever services you use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Telegram bot tokens (numeric:alphanumeric format)
git secrets --add '[0-9]{8,10}:[A-Za-z0-9_-]{35}'

# Stripe keys
git secrets --add 'sk_(live|test)_[A-Za-z0-9]{24,}'

# Generic high-entropy passwords in connection strings
git secrets --add 'password\s*=\s*[^\s]{12,}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "AKIAIOSFODNN7EXAMPLE" &amp;gt; test.txt
git add test.txt
git commit -m "test"
# Output: [ERROR] Matched one or more prohibited patterns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One gotcha: &lt;code&gt;git secrets --install&lt;/code&gt; only sets up hooks in &lt;em&gt;that&lt;/em&gt; repo. For global coverage across all repos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git secrets --install ~/.git-templates/git-secrets
git config --global init.templateDir ~/.git-templates/git-secrets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding trufflehog as a Pre-Commit Hook
&lt;/h2&gt;

&lt;p&gt;I use the &lt;a href="https://pre-commit.com/" rel="noopener noreferrer"&gt;pre-commit&lt;/a&gt; framework for trufflehog since it handles updates and version pinning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .pre-commit-config.yaml
repos:
  - repo: https://github.com/trufflesecurity/trufflehog
    rev: v3.78.1
    hooks:
      - id: trufflehog
        entry: trufflehog git file://. --since-commit HEAD --only-verified --fail
        stages: [commit, push]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--only-verified&lt;/code&gt; flag is important. Without it, trufflehog reports every high-entropy string — UUIDs, hashes, random test data. With it, you only get alerts for secrets that are confirmed active against their respective APIs. This drops false positives from ~30/week to about 1.&lt;/p&gt;

&lt;p&gt;Install and activate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install pre-commit
pre-commit install
pre-commit install --hook-type pre-push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Configuration Mistake That Gives False Confidence
&lt;/h2&gt;

&lt;p&gt;Here’s what tripped me up for months: &lt;strong&gt;git-secrets only scans staged changes by default, not the full file.&lt;/strong&gt; If you have a secret on line 5 and you modify line 50, git-secrets won’t flag it because line 5 isn’t in the diff.&lt;/p&gt;

&lt;p&gt;This matters because secrets often enter a file in one commit and stay there forever. The pre-commit hook only fires on new changes, so existing secrets remain invisible.&lt;/p&gt;

&lt;p&gt;Fix: run a full-repo scan on a schedule. I have this in a weekly cron:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Scan entire repo history
trufflehog git file:///path/to/repo --only-verified --json &amp;gt; /tmp/secrets-audit.json

# Scan all current files (not just diffs)
git secrets --scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I pipe the output to ntfy for notifications. If something shows up, I rotate the credential immediately and use &lt;code&gt;git filter-repo&lt;/code&gt; to purge it from history:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git filter-repo --invert-paths --path secrets.env
# Then force-push and tell collaborators to re-clone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What About GitHub’s Built-in Secret Scanning?
&lt;/h2&gt;

&lt;p&gt;GitHub’s secret scanning (free for public repos, paid for private) is solid but it’s a safety net, not prevention. By the time GitHub alerts you, the secret has already been pushed to a remote. If your repo was public for even 5 seconds, bots have already scraped it — I’ve seen AWS keys exploited within &lt;a href="https://www.comparitech.com/blog/information-security/github-honeypot-experiment/" rel="noopener noreferrer"&gt;4 minutes of being pushed&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pre-commit hooks stop the secret locally. That’s the difference between “we caught it early” and “we need to rotate everything and audit CloudTrail logs.”&lt;/p&gt;

&lt;h2&gt;
  
  
  My Full .pre-commit-config.yaml
&lt;/h2&gt;

&lt;p&gt;Here’s what I run on every project now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;repos:
  - repo: https://github.com/trufflesecurity/trufflehog
    rev: v3.78.1
    hooks:
      - id: trufflehog
        entry: trufflehog git file://. --since-commit HEAD --only-verified --fail
        stages: [commit, push]

  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.4
    hooks:
      - id: gitleaks
        stages: [commit]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I actually dropped git-secrets from the pre-commit config because gitleaks covers similar patterns with better regex coverage and active maintenance. I still keep git-secrets installed globally as a backup layer — defense in depth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Total overhead per commit:&lt;/strong&gt; ab&lt;/p&gt;

</description>
      <category>security</category>
      <category>git</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Built a Free Client-Side Image Compressor (No Upload, Works Offline)</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Tue, 26 May 2026 15:02:21 +0000</pubDate>
      <link>https://dev.to/orthogonalinfo/i-built-a-free-client-side-image-compressor-no-upload-works-offline-46f6</link>
      <guid>https://dev.to/orthogonalinfo/i-built-a-free-client-side-image-compressor-no-upload-works-offline-46f6</guid>
      <description>&lt;p&gt;I got tired of uploading sensitive images to servers just to compress them. TinyPNG is great but requires server upload. Squoosh only does one image at a time.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;QuickShrink&lt;/strong&gt; — a browser-based image compressor where everything happens client-side. Your images never leave your device.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client-side compression&lt;/strong&gt; — uses Canvas API + WebP encoding in-browser&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch processing&lt;/strong&gt; — drag multiple files, compress all at once&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebP output&lt;/strong&gt; — 30-50% smaller than JPEG at same quality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resize + compress&lt;/strong&gt; — set max dimensions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality presets&lt;/strong&gt; — Web, Social Media, Email, Print&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PWA&lt;/strong&gt; — install it, works offline&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why client-side matters
&lt;/h2&gt;

&lt;p&gt;If you are compressing screenshots with credentials, internal docs, or client work — you probably do not want them hitting someone elses server. With QuickShrink, the files stay in your browser tab.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://quickshrink.orthogonal.info" rel="noopener noreferrer"&gt;quickshrink.orthogonal.info&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free tier: 5 images/day. Pro (4.99/mo): unlimited + priority features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;p&gt;Vanilla JS, Canvas API, Web Workers for non-blocking compression, Service Worker for offline support. No framework, loads in under 1 second.&lt;/p&gt;




&lt;p&gt;Would love feedback from fellow devs. What features would make this more useful for your workflow?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tools</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Why I Built a Browser-Only Image Compressor (No Uploads, No Server)</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Tue, 19 May 2026 17:03:03 +0000</pubDate>
      <link>https://dev.to/orthogonalinfo/why-i-built-a-browser-only-image-compressor-no-uploads-no-server-1071</link>
      <guid>https://dev.to/orthogonalinfo/why-i-built-a-browser-only-image-compressor-no-uploads-no-server-1071</guid>
      <description>&lt;p&gt;Most image compression tools require you to upload your photos to a remote server. But what if you are compressing screenshots with sensitive data or client mockups under NDA?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Privacy Problem
&lt;/h2&gt;

&lt;p&gt;When you upload to a compression service, you trust that company to not store, not train AI on, and delete your image. You have no way to verify it.&lt;/p&gt;

&lt;p&gt;For personal photos, acceptable risk. For business documents, medical images, legal screenshots? Real problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Client-Side Compression: How It Works
&lt;/h2&gt;

&lt;p&gt;Modern browsers have powerful image processing APIs (Canvas API, OffscreenCanvas) that can resize and re-encode images entirely in JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;naturalWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;naturalHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Export at reduced quality - never leaves the browser&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// blob is your compressed image&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No fetch calls, no FormData uploads, no server round-trips.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Client-Side vs Server-Side
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Client-side wins when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Images contain confidential information&lt;/li&gt;
&lt;li&gt;You are under NDA or HIPAA compliance&lt;/li&gt;
&lt;li&gt;You want fastest compression (no upload wait)&lt;/li&gt;
&lt;li&gt;Slow or metered internet connection&lt;/li&gt;
&lt;li&gt;You value privacy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Server-side tools may be better when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need advanced algorithms (MozJPEG, AVIF encoding)&lt;/li&gt;
&lt;li&gt;Batch-processing thousands of images via API&lt;/li&gt;
&lt;li&gt;You need specific format conversions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://quickshrink.orthogonal.info" rel="noopener noreferrer"&gt;QuickShrink&lt;/a&gt; to make this dead simple. Drop an image, get a smaller one back. No signup, no uploads, no tracking.&lt;/p&gt;

&lt;p&gt;You can verify yourself: open DevTools Network tab and watch. Zero outbound requests during compression.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What is your approach to image optimization in your projects? Do you trust upload-based tools with sensitive content?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>privacy</category>
      <category>javascript</category>
      <category>tools</category>
    </item>
    <item>
      <title>I built a privacy-first image compressor that runs entirely in your browser</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Thu, 14 May 2026 17:02:02 +0000</pubDate>
      <link>https://dev.to/orthogonalinfo/i-built-a-privacy-first-image-compressor-that-runs-entirely-in-your-browser-1144</link>
      <guid>https://dev.to/orthogonalinfo/i-built-a-privacy-first-image-compressor-that-runs-entirely-in-your-browser-1144</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Every time I needed to compress an image before deploying, I had to choose between:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cloud tools&lt;/strong&gt; that upload my files to unknown servers (privacy concern with client work)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI tools&lt;/strong&gt; like &lt;code&gt;imagemagick&lt;/code&gt; that require remembering flags every time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paid tools&lt;/strong&gt; with freemium limits that kick in at the worst moment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I just wanted something I could bookmark and use in 3 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: QuickShrink
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://quickshrink.orthogonal.info" rel="noopener noreferrer"&gt;QuickShrink&lt;/a&gt; is a free, browser-based image compressor. Zero uploads — all processing happens client-side using the Canvas API and modern compression algorithms.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Drag and drop (or click to upload) any image&lt;/li&gt;
&lt;li&gt;Adjust quality slider if needed&lt;/li&gt;
&lt;li&gt;Download the compressed version&lt;/li&gt;
&lt;li&gt;Your files never leave your machine&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why browser-based matters:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy&lt;/strong&gt;: No server ever sees your images. Important for NDA work, client mockups, medical images, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: No upload/download wait. Compression is instant for most files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No limits&lt;/strong&gt;: No daily caps, no signup walls, no watermarks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works offline&lt;/strong&gt;: Once loaded, it works without internet.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical bits
&lt;/h2&gt;

&lt;p&gt;The compression uses the browser Canvas API for lossy compression (JPEG quality reduction) and leverages modern browser image codecs. For PNGs, it re-encodes with optimized settings. Typical results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4MB JPEG → ~800KB (80% reduction at quality 80)&lt;/li&gt;
&lt;li&gt;Hero images for blogs → usually 60-70% smaller&lt;/li&gt;
&lt;li&gt;Screenshots with text → 50-60% reduction&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who is this for?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Devs who need quick image compression before &lt;code&gt;git push&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Bloggers optimizing hero images&lt;/li&gt;
&lt;li&gt;Anyone working with sensitive/client images who can not use cloud tools&lt;/li&gt;
&lt;li&gt;People tired of signing up for yet another SaaS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;👉 &lt;a href="https://quickshrink.orthogonal.info" rel="noopener noreferrer"&gt;quickshrink.orthogonal.info&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No signup. No ads. Just compression.&lt;/p&gt;




&lt;p&gt;Feedback welcome — what features would make this more useful for your workflow?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>javascript</category>
      <category>tools</category>
    </item>
    <item>
      <title>Best TinyPNG Alternatives in 2026 (Free &amp; Privacy-First)</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Thu, 07 May 2026 17:03:07 +0000</pubDate>
      <link>https://dev.to/orthogonalinfo/best-tinypng-alternatives-in-2026-free-privacy-first-3n8</link>
      <guid>https://dev.to/orthogonalinfo/best-tinypng-alternatives-in-2026-free-privacy-first-3n8</guid>
      <description>&lt;p&gt;Looking for a &lt;strong&gt;TinyPNG alternative&lt;/strong&gt; that's free, private, and doesn't require uploads? Here are the best options in 2026 — including one that compresses images entirely in your browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Look Beyond TinyPNG?
&lt;/h2&gt;

&lt;p&gt;TinyPNG is great, but it has limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free tier limited to 20 images/month via API (500 via web)&lt;/li&gt;
&lt;li&gt;Files are uploaded to their servers — privacy concern for sensitive images&lt;/li&gt;
&lt;li&gt;No WebP output from the free web tool&lt;/li&gt;
&lt;li&gt;Pro pricing starts at $25/year&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need more flexibility, better privacy, or different output formats, these alternatives deliver.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. QuickShrink (Best for Privacy)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Price:&lt;/strong&gt; Free (5 images/day) | Pro $4.99/mo&lt;br&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://quickshrink.orthogonal.info" rel="noopener noreferrer"&gt;quickshrink.orthogonal.info&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key advantage:&lt;/strong&gt; 100% browser-based. Your images never leave your device. Zero uploads, zero server processing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supports PNG, JPEG, WebP output&lt;/li&gt;
&lt;li&gt;Adjustable quality slider&lt;/li&gt;
&lt;li&gt;Instant results — no waiting for server response&lt;/li&gt;
&lt;li&gt;No account required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Designers and developers who handle client assets or sensitive images and don't want them on third-party servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Squoosh (Google)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Price:&lt;/strong&gt; Free&lt;br&gt;
&lt;strong&gt;Website:&lt;/strong&gt; squoosh.app&lt;/p&gt;

&lt;p&gt;Google's open-source image compressor. Also browser-based with side-by-side preview. Excellent for single images but no batch processing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advanced codec options (MozJPEG, AVIF, WebP)&lt;/li&gt;
&lt;li&gt;Real-time quality comparison&lt;/li&gt;
&lt;li&gt;No batch mode — one image at a time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Developers who want fine-grained control over codec settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. ShortPixel
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Price:&lt;/strong&gt; Free (100 images/month) | From $3.99/mo&lt;br&gt;
&lt;strong&gt;Website:&lt;/strong&gt; shortpixel.com&lt;/p&gt;

&lt;p&gt;Server-based compression with WordPress plugin. Good for bulk optimization of existing sites.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress integration&lt;/li&gt;
&lt;li&gt;Lossy, glossy, and lossless modes&lt;/li&gt;
&lt;li&gt;CDN delivery option&lt;/li&gt;
&lt;li&gt;Files uploaded to their servers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; WordPress site owners who want set-and-forget optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Compressor.io
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Price:&lt;/strong&gt; Free (limited) | Pro $50/year&lt;br&gt;
&lt;strong&gt;Website:&lt;/strong&gt; compressor.io&lt;/p&gt;

&lt;p&gt;Clean interface with good compression ratios. Supports SVG compression which most tools don't.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supports JPEG, PNG, GIF, SVG, WebP&lt;/li&gt;
&lt;li&gt;Up to 10MB file size&lt;/li&gt;
&lt;li&gt;Server-side processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Designers working with multiple formats including SVG.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. ImageOptim (Mac Only)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Price:&lt;/strong&gt; Free (desktop app) | API from $12/mo&lt;br&gt;
&lt;strong&gt;Website:&lt;/strong&gt; imageoptim.com&lt;/p&gt;

&lt;p&gt;Desktop app that strips metadata and optimizes locally. Mac-only for the free version.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fully offline processing&lt;/li&gt;
&lt;li&gt;Strips EXIF data automatically&lt;/li&gt;
&lt;li&gt;Drag-and-drop batch processing&lt;/li&gt;
&lt;li&gt;Mac only (no Windows/Linux)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Mac users who prefer desktop workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Privacy&lt;/th&gt;
&lt;th&gt;Batch&lt;/th&gt;
&lt;th&gt;Free Limit&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;QuickShrink&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Browser-only&lt;/td&gt;
&lt;td&gt;Coming soon&lt;/td&gt;
&lt;td&gt;5/day&lt;/td&gt;
&lt;td&gt;$4.99/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TinyPNG&lt;/td&gt;
&lt;td&gt;❌ Upload&lt;/td&gt;
&lt;td&gt;✅ 20 files&lt;/td&gt;
&lt;td&gt;500/month web&lt;/td&gt;
&lt;td&gt;$25/yr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Squoosh&lt;/td&gt;
&lt;td&gt;✅ Browser-only&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ShortPixel&lt;/td&gt;
&lt;td&gt;❌ Upload&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;100/month&lt;/td&gt;
&lt;td&gt;$3.99/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compressor.io&lt;/td&gt;
&lt;td&gt;❌ Upload&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;$50/yr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ImageOptim&lt;/td&gt;
&lt;td&gt;✅ Local&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;Free (Mac)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Verdict
&lt;/h2&gt;

&lt;p&gt;If &lt;strong&gt;privacy matters&lt;/strong&gt; (and it should — especially with client work), QuickShrink is the strongest TinyPNG alternative. Your images stay on your device, compression happens in-browser, and it's fast.&lt;/p&gt;

&lt;p&gt;For power users who need codec-level control, Squoosh is excellent but limited to one file at a time.&lt;/p&gt;

&lt;p&gt;What's your go-to image compression tool? Let me know in the comments 👇&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webperf</category>
      <category>productivity</category>
      <category>tools</category>
    </item>
    <item>
      <title>Build an Unusual Options Activity Scanner With Python and Free Data</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Wed, 15 Apr 2026 18:12:49 +0000</pubDate>
      <link>https://dev.to/orthogonalinfo/build-an-unusual-options-activity-scanner-with-python-and-free-data-kka</link>
      <guid>https://dev.to/orthogonalinfo/build-an-unusual-options-activity-scanner-with-python-and-free-data-kka</guid>
      <description>&lt;p&gt;Last month I noticed something odd: SMCI options volume spiked to 8x its 20-day average on a random Tuesday afternoon. No news. No earnings. Three days later, the stock jumped 14% on a surprise partnership announcement. Someone knew.&lt;/p&gt;

&lt;p&gt;Unusual options activity (UOA) — when volume on a specific contract explodes beyond normal levels — is one of the most reliable signals that informed money is positioning. Services like Unusual Whales and Cheddar Flow charge $40-80/month to show you this data. I built my own scanner for free in about 200 lines of Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Counts as "Unusual"
&lt;/h2&gt;

&lt;p&gt;Before writing code, you need a working definition. I use three filters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Volume/Open Interest ratio &amp;gt; 3.0&lt;/strong&gt; — When daily volume on a contract is 3x or more the existing open interest, that’s new money entering, not existing positions rolling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Premium &amp;gt; $25,000&lt;/strong&gt; — Filters out noise. A retail trader buying 5 contracts of a cheap OTM option isn’t a signal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Days to expiration between 7-90&lt;/strong&gt; — Too short means gamma scalping. Too long means it’s likely a hedge, not a directional bet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren’t perfect — no filter is. But they eliminate about 95% of the noise and leave you with 10-30 actionable alerts per day instead of thousands.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data Problem (and Three Free Solutions)
&lt;/h2&gt;

&lt;p&gt;Options data is expensive. Real-time feeds from OPRA cost thousands per month. But for a daily scanner that runs after market close, you don’t need real-time. Here are three approaches I tested:&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Tradier Sandbox API (My Pick)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://tradier.com" rel="noopener noreferrer"&gt;Tradier&lt;/a&gt; offers a free sandbox API that includes delayed options chains with volume and open interest. The delay is 15 minutes, which is fine for an end-of-day scanner. Rate limit: 120 requests/minute on the free tier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;TRADIER_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_SANDBOX_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Free at developer.tradier.com
&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://sandbox.tradier.com/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;HEADERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TRADIER_TOKEN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Accept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_options_chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="c1"&gt;# First get expiration dates
&lt;/span&gt;    &lt;span class="n"&gt;exp_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/markets/options/expirations&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exp_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;dates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expirations&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;all_contracts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;exp_date&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dates&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;  &lt;span class="c1"&gt;# Next 6 expirations
&lt;/span&gt;        &lt;span class="n"&gt;chain_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/markets/options/chains&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expiration&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;exp_date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chain_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;options&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;option&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
        &lt;span class="n"&gt;all_contracts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;all_contracts&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each contract in the response includes &lt;code&gt;volume&lt;/code&gt;, &lt;code&gt;open_interest&lt;/code&gt;, &lt;code&gt;last&lt;/code&gt;, and &lt;code&gt;option_type&lt;/code&gt;. That’s everything you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Yahoo Finance (yfinance)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;yfinance&lt;/code&gt; library pulls options data directly. No API key needed. The catch: it’s slow (one request per ticker) and Yahoo occasionally rate-limits aggressive scraping.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;yfinance&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;yf&lt;/span&gt;

&lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AAPL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;exp_date&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;option_chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exp_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;calls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;calls&lt;/span&gt;  &lt;span class="c1"&gt;# DataFrame with volume, openInterest, etc.
&lt;/span&gt;    &lt;span class="n"&gt;puts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used this initially but switched to Tradier. Yahoo’s data occasionally has gaps — missing volume on contracts that clearly traded — and the rate limiting makes scanning 100+ symbols painful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 3: Polygon.io Free Tier
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://polygon.io" rel="noopener noreferrer"&gt;Polygon.io&lt;/a&gt; gives you 5 API calls/minute on the free tier. That’s rough for options scanning since you need one call per expiration per symbol. I’d only recommend this if you’re scanning fewer than 20 symbols.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scanner: 200 Lines That Actually Work
&lt;/h2&gt;

&lt;p&gt;Here’s the core logic. I run this daily at 4:30 PM ET via cron.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scan_unusual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contracts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;min_vol_oi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;min_premium&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_dte&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Filter options contracts for unusual activity.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;today&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;unusual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;contracts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;volume&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;volume&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;oi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open_interest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;last_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;last&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="c1"&gt;# Skip dead contracts
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;volume&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;last_price&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="c1"&gt;# Calculate days to expiration
&lt;/span&gt;        &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expiration_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%Y-%m-%d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;dte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;today&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dte&lt;/span&gt;  &lt;span class="n"&gt;max_dte&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="c1"&gt;# Volume/OI ratio (handle zero OI)
&lt;/span&gt;        &lt;span class="n"&gt;vol_oi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;volume&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;vol_oi&lt;/span&gt;  &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;by_symbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;call_premium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;put_premium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;alerts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;call_premium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;call&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;put_premium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;by_symbol&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;symbol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]][&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;premium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;by_symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;call_premium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;put_premium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;bull_pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;call_premium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
            &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sym&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bullish_pct&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bull_pct&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total_premium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Delivery.&lt;/strong&gt; I push the top alerts to a Telegram channel using a bot. You could also use &lt;a href="https://ntfy.sh" rel="noopener noreferrer"&gt;ntfy.sh&lt;/a&gt; (free, self-hostable) or plain email via &lt;code&gt;smtplib&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned Running This for 6 Months
&lt;/h2&gt;

&lt;p&gt;A few hard-earned observations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UOA predicts direction roughly 60% of the time.&lt;/strong&gt; That’s better than a coin flip, but it’s not magic. Don’t bet the farm on any single alert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sector clustering matters more than individual signals.&lt;/strong&gt; When you see unusual call activity across 5 semiconductor names on the same day, that’s more meaningful than a single NVDA spike.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Earnings week is noise.&lt;/strong&gt; I exclude any ticker with earnings within 5 trading days. The UOA around earnings is mostly people buying lottery tickets, not informed positioning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Friday afternoon sweeps are the best signals.&lt;/strong&gt; Big money placing bets late Friday when retail has checked out? That often moves Monday-Tuesday.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Full Setup on a Raspberry Pi
&lt;/h2&gt;

&lt;p&gt;My scanner runs on a &lt;a href="https://www.amazon.com/Raspberry-Model-2GB-Quad-Core-Bluetooth/dp/B0CPWH8FL9?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;Raspberry Pi 5&lt;/a&gt; that also handles my other homelab scripts. Total resource usage: ~40MB RAM, finishes in under 10 minutes. Cron triggers it at 4:30 PM ET, and I get a Telegram notification with the day’s unusual activity by 4:40 PM.&lt;/p&gt;

&lt;p&gt;If you want a more portable development environment, a &lt;a href="https://www.amazon.com/Samsung-T7-Portable-External-MU-PC1T0T/dp/B0874XN4D8?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;Samsung T7 portable SSD&lt;/a&gt; makes it easy to carry your full dev setup between machines — I keep my Python environments and data on one so I can plug into any workstation.&lt;/p&gt;

&lt;p&gt;For going deeper on the quantitative side, &lt;a href="https://www.amazon.com/Python-Finance-Mastering-Data-Driven/dp/1492024333?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;Python for Finance by Yves Hilpisch&lt;/a&gt; is the best resource I’ve found for turning signals like these into a backtestable strategy. It covers everything from data handling to options pricing models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should You Actually Trade on UOA?
&lt;/h2&gt;

&lt;p&gt;Honestly? Maybe. I use it as one input alongside technicals and macro. The signals are real — informed money does move through the options market before news drops. But “informed” doesn’t mean “always right,” and options flow data is increasingly gamed by sophisticated players who know retail is watching.&lt;/p&gt;

&lt;p&gt;The real value for me has been understanding market sentiment. When I see aggressive call buying across financials before an FOMC meeting, that tells me something about positioning — even if I don’t trade it directly.&lt;/p&gt;

&lt;p&gt;If you want daily market intelligence covering signals like these, I run a free Telegram channel: &lt;a href="https://t.me/alphasignal822" rel="noopener noreferrer"&gt;Join Alpha Signal&lt;/a&gt; for free market analysis, sector rotation tracking, and macro breakdowns.&lt;/p&gt;

&lt;p&gt;The full scanner code is about 200 lines. I’m considering open-sourcing it — if there’s interest, I’ll throw it on GitHub. For now, the snippets above give you everything you need to build your own.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Related: &lt;a href="https://orthogonal.info/track-congress-trades-with-python-free-sec-data/" rel="noopener noreferrer"&gt;Track Congress Trades with Python&lt;/a&gt; | &lt;a href="https://orthogonal.info/insider-trading-detector-with-python-free-sec-data/" rel="noopener noreferrer"&gt;Insider Trading Detector with Python&lt;/a&gt; | &lt;a href="https://orthogonal.info/algorithmic-trading-engineering-guide/" rel="noopener noreferrer"&gt;Algorithmic Trading for Engineers&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Full disclosure: Amazon links above are affiliate links.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>trading</category>
      <category>finance</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Full Stack Monitoring: A Security-First Approach</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Wed, 15 Apr 2026 16:46:34 +0000</pubDate>
      <link>https://dev.to/orthogonalinfo/full-stack-monitoring-a-security-first-approach-2j6o</link>
      <guid>https://dev.to/orthogonalinfo/full-stack-monitoring-a-security-first-approach-2j6o</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Full stack monitoring is essential for modern architectures, encompassing infrastructure, applications, and user experience. A security-first approach ensures that monitoring not only detects performance issues but also safeguards against threats. By integrating DevSecOps principles, you can create a scalable, resilient, and secure monitoring strategy tailored for Kubernetes environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick Answer:&lt;/strong&gt; Full stack monitoring is the practice of observing every layer of your system, from infrastructure to user experience, with a focus on performance and security. It’s critical for detecting issues early and maintaining a secure, reliable environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction to Full Stack Monitoring
&lt;/h2&gt;

&lt;p&gt;Imagine your application stack as a high-performance race car. The engine (infrastructure), the driver (application), and the tires (user experience) all need to work in harmony for the car to perform well. Now imagine trying to diagnose a problem during a race without any telemetry—no speedometer, no engine diagnostics, no tire pressure readings. That’s what running a modern system without full stack monitoring feels like.&lt;/p&gt;

&lt;p&gt;Full stack monitoring is the practice of observing every layer of your system, from the underlying infrastructure to the end-user experience. It’s not just about ensuring uptime; it’s about understanding how each component interacts and identifying issues before they escalate. In today’s threat landscape, a security-first approach to monitoring is non-negotiable. Attackers don’t just exploit vulnerabilities—they exploit blind spots. (For network-layer visibility, see &lt;a href="https://orthogonal.info/mastering-kubernetes-security-network-policies-service-mesh/" rel="noopener noreferrer"&gt;Kubernetes Network Policies and Service Mesh Security&lt;/a&gt;.) Monitoring every layer ensures you’re not flying blind.&lt;/p&gt;

&lt;p&gt;Key components of full stack monitoring include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure Monitoring:&lt;/strong&gt; Observing servers, networks, and cloud resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Monitoring:&lt;/strong&gt; Tracking application performance, APIs, and microservices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Experience Monitoring:&lt;/strong&gt; Measuring how end-users interact with your application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here’s the kicker: monitoring without a security-first mindset is like locking your front door while leaving the windows wide open. Let’s explore why security-first monitoring is critical and how it integrates seamlessly with Kubernetes and DevSecOps principles.&lt;/p&gt;

&lt;p&gt;Full stack monitoring also provides the foundation for proactive system management. By collecting and analyzing data across all layers, teams can identify trends, predict potential failures, and optimize performance. For example, if your application experiences a sudden spike in database queries, monitoring can help pinpoint whether the issue lies in the application code, database configuration, or user behavior.&lt;/p&gt;

&lt;p&gt;Additionally, full stack monitoring is invaluable for compliance. Many industries, such as finance and healthcare, require detailed logs and metrics to demonstrate adherence to regulations. A robust monitoring strategy ensures you have the necessary data to pass audits and maintain trust with stakeholders.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Pro Tip:&lt;/strong&gt; Start by mapping out your entire stack and identifying the most critical components to monitor. This will help you prioritize resources and avoid being overwhelmed by data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s a simple example of setting up a basic monitoring script using Python to track CPU and memory usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;psutil&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;monitor_system&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cpu_usage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cpu_percent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;memory_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;virtual_memory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CPU Usage: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cpu_usage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Memory Usage: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;memory_info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;percent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;monitor_system&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script provides a starting point for understanding system resource usage, which can be extended to include additional metrics or integrated with a larger monitoring framework.&lt;/p&gt;

&lt;p&gt;Another practical example is using a cloud-based monitoring service like AWS CloudWatch or Google Cloud Operations Suite. These tools provide built-in integrations with your cloud infrastructure, making it easier to monitor resources like virtual machines, databases, and storage buckets. For instance, you can set up alarms in AWS CloudWatch to notify your team when CPU utilization exceeds a certain threshold, helping you respond to performance issues before they impact users.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Common Pitfall:&lt;/strong&gt; Avoid overloading your monitoring system with unnecessary metrics. Too much data can obscure critical insights and overwhelm your team.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To address edge cases, consider scenarios where your monitoring tools fail or produce incomplete data. For example, if your monitoring system relies on a single server and that server crashes, you lose visibility into your stack. Implementing redundancy and failover mechanisms for your monitoring infrastructure ensures continuous observability.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Role of Full Stack Monitoring in Kubernetes
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;If you're hardening your cluster alongside monitoring, check out the &lt;a href="https://orthogonal.info/kubernetes-security-checklist-for-production-2026/" rel="noopener noreferrer"&gt;Kubernetes Security Checklist for Production&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Kubernetes is a game-changer for modern application deployment, but it’s also a monitoring nightmare. Pods come and go, nodes scale dynamically, and workloads are distributed across clusters. Traditional monitoring tools struggle to keep up with this level of complexity.&lt;/p&gt;

&lt;p&gt;Full stack monitoring in Kubernetes involves tracking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cluster Health:&lt;/strong&gt; Monitoring nodes, pods, and resource utilization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Performance:&lt;/strong&gt; Observing how services interact and identifying bottlenecks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Events:&lt;/strong&gt; Detecting unauthorized access, privilege escalations, and misconfigurations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tools like &lt;strong&gt;Prometheus&lt;/strong&gt; and &lt;strong&gt;Grafana&lt;/strong&gt; are staples for Kubernetes monitoring. Prometheus collects metrics from Kubernetes components, while Grafana visualizes them in dashboards. But these tools are just the start. For a security-first approach, you’ll want to integrate solutions like &lt;strong&gt;Falco&lt;/strong&gt; for runtime security and &lt;strong&gt;Open Policy Agent (OPA)&lt;/strong&gt; for policy enforcement.&lt;/p&gt;

&lt;p&gt;In a real-world scenario, consider a Kubernetes cluster running a microservices-based e-commerce application. Without proper monitoring, a sudden increase in traffic could overwhelm the payment service, causing delays or failures. By using Prometheus to monitor pod resource usage and Grafana to visualize trends, you can identify the issue and scale the affected service before it impacts users.&lt;/p&gt;

&lt;p&gt;Another critical aspect is monitoring Kubernetes API server logs. These logs can reveal unauthorized access attempts or misconfigured RBAC (Role-Based Access Control) policies. For example, if a developer accidentally grants admin privileges to a service account, monitoring tools can alert you to the potential security risk.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Security Note:&lt;/strong&gt; The default configurations of many Kubernetes monitoring tools are not secure. Always enable authentication and encryption for Prometheus endpoints and Grafana dashboards.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s an example of setting up Prometheus to scrape metrics securely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scrape_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15s&lt;/span&gt;
  &lt;span class="na"&gt;evaluation_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15s&lt;/span&gt;

&lt;span class="na"&gt;scrape_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kubernetes-nodes'&lt;/span&gt;
    &lt;span class="na"&gt;scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https&lt;/span&gt;
    &lt;span class="na"&gt;tls_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ca_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/prometheus/ssl/ca.crt&lt;/span&gt;
      &lt;span class="na"&gt;cert_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/prometheus/ssl/prometheus.crt&lt;/span&gt;
      &lt;span class="na"&gt;key_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/prometheus/ssl/prometheus.key&lt;/span&gt;
    &lt;span class="na"&gt;kubernetes_sd_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration ensures that Prometheus communicates securely with Kubernetes nodes using TLS.&lt;/p&gt;

&lt;p&gt;When implementing monitoring in Kubernetes, it’s essential to account for the ephemeral nature of containers. Logs and metrics should be centralized to prevent data loss when pods are terminated. Tools like Fluentd and Elasticsearch can help aggregate logs, while Prometheus handles metrics collection.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Pro Tip:&lt;/strong&gt; Use Kubernetes namespaces to organize monitoring resources. For example, create a dedicated namespace for Prometheus, Grafana, and other observability tools to simplify management.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To further enhance security, consider using network policies to restrict communication between monitoring tools and other components. For example, you can use Calico or Cilium to define policies that allow Prometheus to scrape metrics only from specific namespaces or pods.&lt;/p&gt;

&lt;h2&gt;
  
  
  DevSecOps and Full Stack Monitoring: A Perfect Match
&lt;/h2&gt;

&lt;p&gt;DevSecOps is the philosophy of integrating security into every phase of the development lifecycle. When applied to monitoring, it means embedding security checks and alerts into your observability stack. This approach not only improves security but also enhances reliability and performance.&lt;/p&gt;

&lt;p&gt;Here’s how DevSecOps principles enhance full stack monitoring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shift Left:&lt;/strong&gt; Monitor security metrics during development, not just in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation:&lt;/strong&gt; Use CI/CD pipelines to deploy and update monitoring configurations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaboration:&lt;/strong&gt; Share monitoring insights across development, operations, and security teams.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, integrating &lt;strong&gt;SonarQube&lt;/strong&gt; into your CI/CD pipeline can help identify code vulnerabilities early. Similarly, tools like &lt;strong&gt;Datadog&lt;/strong&gt; and &lt;strong&gt;New Relic&lt;/strong&gt; can provide real-time insights into application performance and security.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Pro Tip:&lt;/strong&gt; Use Infrastructure as Code (IaC) tools like Terraform to manage your monitoring stack. This ensures consistency across environments and makes it easier to audit changes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s an example of using Terraform to deploy a Prometheus and Grafana stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;helm_release&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prometheus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prometheus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;chart&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prometheus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://prometheus-community.github.io/helm-charts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;namespace&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;monitoring&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;helm_release&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grafana&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grafana&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;chart&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grafana&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://grafana.github.io/helm-charts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;namespace&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;monitoring&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Terraform configuration deploys Prometheus and Grafana using Helm charts, ensuring a consistent setup across environments.&lt;/p&gt;

&lt;p&gt;Another key aspect of DevSecOps is integrating security scanning into your monitoring pipeline. Tools like Aqua Security and Trivy can scan container images for vulnerabilities, while Falco can detect runtime anomalies. For example, if a container starts running an unexpected process, Falco can trigger an alert and even terminate the container to prevent further damage.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🔒 Security Note:&lt;/strong&gt; Always use signed container images from trusted sources to minimize the risk of deploying compromised software.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Advanced Monitoring Techniques
&lt;/h2&gt;

&lt;p&gt;While traditional monitoring focuses on metrics and logs, advanced techniques like distributed tracing and anomaly detection can take your observability to the next level. Distributed tracing tools such as Jaeger and Zipkin allow you to track requests as they flow through microservices, providing insights into latency and bottlenecks.&lt;/p&gt;

&lt;p&gt;Anomaly detection, powered by machine learning, can identify unusual patterns in your metrics. For example, if your application suddenly experiences a spike in error rates during off-peak hours, anomaly detection tools can flag this as a potential issue. Tools like Elastic APM and Dynatrace provide built-in anomaly detection capabilities. For a deeper dive into open-source security monitoring, see our guide on &lt;a href="https://orthogonal.info/enterprise-security-at-home-wazuh-suricata-setup/" rel="noopener noreferrer"&gt;setting up Wazuh and Suricata for enterprise-grade detection&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Pro Tip:&lt;/strong&gt; Combine distributed tracing with metrics and logs for a comprehensive observability strategy. This triad ensures you capture every aspect of your system’s behavior.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s an example of configuring Jaeger for distributed tracing in Kubernetes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jaeger-config&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;monitoring&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;config.yaml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;collector:&lt;/span&gt;
      &lt;span class="s"&gt;zipkin:&lt;/span&gt;
        &lt;span class="s"&gt;http-port: 9411&lt;/span&gt;
    &lt;span class="s"&gt;storage:&lt;/span&gt;
      &lt;span class="s"&gt;type: memory&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration sets up Jaeger to collect traces and store them in memory, suitable for development environments.&lt;/p&gt;

&lt;p&gt;Advanced monitoring also includes synthetic monitoring, where simulated user interactions are used to test application performance. For example, you can use tools like Selenium or Puppeteer to simulate user actions such as logging in or making a purchase. These tests can be scheduled to run periodically, ensuring your application remains functional under various conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Trends in Full Stack Monitoring
&lt;/h2&gt;

&lt;p&gt;As technology evolves, so does the field of monitoring. Emerging trends include the use of AI and predictive analytics to anticipate issues before they occur. For example, AI-driven monitoring tools can analyze historical data to predict when a server might fail or when traffic spikes are likely to occur.&lt;/p&gt;

&lt;p&gt;Another trend is the integration of observability with chaos engineering. Tools like Gremlin allow you to simulate failures in your system, testing its resilience and ensuring your monitoring tools can detect and respond to these events effectively.&lt;/p&gt;

&lt;p&gt;Finally, edge computing is reshaping monitoring strategies. With data being processed closer to users, monitoring tools must adapt to decentralized architectures. Tools like Prometheus and Grafana are evolving to support edge deployments, ensuring visibility across distributed systems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Pro Tip:&lt;/strong&gt; Stay ahead of the curve by experimenting with AI-driven monitoring tools and chaos engineering practices. These approaches can significantly enhance your system’s resilience and observability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🛠️ Recommended Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tools and books mentioned in (or relevant to) this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/dp/1617297615?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;Kubernetes in Action, 2nd Edition&lt;/a&gt; — The definitive guide to deploying and managing K8s in production ($45-55)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/dp/1492083658?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;Learning Helm&lt;/a&gt; — Managing apps on Kubernetes with the Helm package manager ($35-45)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/dp/1492081736?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;Hacking Kubernetes&lt;/a&gt; — Threat-driven analysis and defense of K8s clusters ($40-50)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/dp/1617297275?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;GitOps and Kubernetes&lt;/a&gt; — Continuous deployment with Argo CD, Jenkins X, and Flux ($40-50)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is full stack monitoring?
&lt;/h3&gt;

&lt;p&gt;Full stack monitoring is the practice of observing every layer of a system, including infrastructure, applications, and user experience. It ensures optimal performance and security by identifying issues early and understanding how different components interact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is a security-first approach important in monitoring?
&lt;/h3&gt;

&lt;p&gt;A security-first approach ensures that monitoring not only detects performance issues but also safeguards against potential threats. Attackers often exploit blind spots, so monitoring every layer of the system helps prevent vulnerabilities from being overlooked.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the key components of full stack monitoring?
&lt;/h3&gt;

&lt;p&gt;The key components include infrastructure monitoring (servers, networks, cloud resources), application monitoring (performance, APIs, microservices), and user experience monitoring (how end-users interact with the application).&lt;/p&gt;

&lt;h3&gt;
  
  
  How does full stack monitoring integrate with DevSecOps principles?
&lt;/h3&gt;

&lt;p&gt;By integrating DevSecOps principles, full stack monitoring becomes a proactive tool for security and performance. It ensures that monitoring strategies are scalable, resilient, and tailored for environments like Kubernetes, aligning development, security, and operations teams.&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>TrueNAS Setup Guide: Enterprise Security at Home</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Tue, 14 Apr 2026 16:47:13 +0000</pubDate>
      <link>https://dev.to/orthogonalinfo/truenas-setup-guide-enterprise-security-at-home-501</link>
      <guid>https://dev.to/orthogonalinfo/truenas-setup-guide-enterprise-security-at-home-501</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; TrueNAS is a powerful storage solution for homelabs, offering enterprise-grade features like ZFS, encryption, and snapshots. This guide walks you through setting up TrueNAS securely, from hardware selection to implementing firewalls and VPNs. By following these steps, you’ll ensure your data is safe, accessible, and future-proof.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Quick Answer:&lt;/strong&gt; TrueNAS is the best choice for secure, scalable storage in a homelab. With proper setup, including encryption, access controls, and regular updates, you can achieve enterprise-level security at home.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction to TrueNAS and Homelab Security
&lt;/h2&gt;

&lt;p&gt;It started with a simple question: “Why am I trusting a random cloud provider with my personal data?” That thought led me down the rabbit hole of homelab storage solutions, and eventually to TrueNAS. TrueNAS, with its ZFS foundation, enterprise-grade features, and open-source roots, quickly became my go-to choice for secure, reliable storage.&lt;/p&gt;

&lt;p&gt;TrueNAS is more than just a NAS (Network Attached Storage); it’s a full-fledged storage operating system. Whether you’re running TrueNAS CORE or SCALE, you get features like snapshots, replication, and encryption—tools you’d typically find in enterprise environments. But here’s the catch: with great power comes great responsibility. Misconfiguring TrueNAS can leave your data vulnerable to attacks or corruption.&lt;/p&gt;

&lt;p&gt;In this guide, I’ll show you how to set up TrueNAS in your homelab with a security-first mindset. We’ll cover everything from hardware selection to implementing firewalls and VPNs. By the end, you’ll have a robust, secure storage solution that rivals enterprise setups—scaled down for personal use.&lt;/p&gt;

&lt;p&gt;Homelab security is often overlooked, but it’s just as critical as the security of enterprise systems. Cyberattacks, ransomware, and data breaches are no longer limited to large corporations. Even personal setups can be targeted, especially if they’re improperly configured or exposed to the internet. TrueNAS provides a solid foundation for securing your data, but it’s up to you to implement best practices and maintain vigilance.&lt;/p&gt;

&lt;p&gt;One of the key benefits of TrueNAS is its ability to scale with your needs. Whether you’re a hobbyist storing family photos or a developer managing terabytes of project data, TrueNAS can adapt to your requirements. However, scaling also introduces complexity, which makes proper planning and configuration even more important. This guide will help you navigate these challenges and build a system that’s both secure and scalable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Planning Your TrueNAS Setup
&lt;/h2&gt;

&lt;p&gt;Before diving into installation, you need to plan your setup. A well-thought-out plan will save you headaches later, especially when it comes to scaling or troubleshooting. Here’s what you need to consider:&lt;/p&gt;

&lt;h3&gt;
  
  
  Hardware Requirements and Recommendations
&lt;/h3&gt;

&lt;p&gt;TrueNAS can run on a variety of hardware, but not all setups are created equal. For 2025 and beyond, here are my recommendations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPU:&lt;/strong&gt; At least a quad-core processor. Intel Xeon or AMD Ryzen are excellent choices for ECC memory support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAM:&lt;/strong&gt; Minimum 16GB, but 32GB+ is recommended for ZFS deduplication and caching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage:&lt;/strong&gt; Use enterprise-grade HDDs (e.g., Seagate IronWolf Pro or WD Red Pro) for reliability. SSDs are great for caching or fast datasets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NIC:&lt;/strong&gt; A 1GbE NIC is sufficient for most homelabs, but consider 10GbE if you’re dealing with large data transfers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 Pro Tip: Always use ECC (Error-Correcting Code) memory if your motherboard supports it. ZFS relies heavily on RAM, and ECC ensures data integrity by preventing bit-flipping errors.&lt;/p&gt;

&lt;p&gt;When selecting hardware, consider future-proofing your setup. For example, if you anticipate needing more storage in the future, choose a motherboard with additional SATA or NVMe slots. Similarly, if you plan to run virtual machines or containers on TrueNAS SCALE, invest in a CPU with higher core counts and better multi-threading capabilities.&lt;/p&gt;

&lt;p&gt;Another important consideration is power consumption. Homelabs often run 24/7, so energy-efficient components can save you money in the long run. Look for CPUs and drives with low power draw, and consider using a power-efficient PSU (Power Supply Unit) with an 80 Plus Gold or Platinum rating.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing the Right TrueNAS Version
&lt;/h3&gt;

&lt;p&gt;TrueNAS comes in two flavors: CORE and SCALE. Here’s a quick comparison to help you decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TrueNAS CORE:&lt;/strong&gt; Based on FreeBSD, it’s stable and battle-tested. Ideal for traditional NAS use cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TrueNAS SCALE:&lt;/strong&gt; Linux-based with Kubernetes support. Perfect for running containers and virtual machines alongside your storage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re planning to integrate your NAS with Docker or Kubernetes, go with SCALE. Otherwise, CORE is a solid choice for pure storage needs.&lt;/p&gt;

&lt;p&gt;💡 Pro Tip: If you’re unsure which version to choose, start with TrueNAS CORE. You can always migrate to SCALE later if your needs evolve. The TrueNAS community forums are also a great resource for advice and troubleshooting.&lt;/p&gt;

&lt;p&gt;It’s worth noting that TrueNAS SCALE is relatively new compared to CORE, so some features may still be in development. If you require cutting-edge functionality like container orchestration, SCALE is the way to go. However, if you prioritize stability and a proven track record, CORE is the safer bet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Network Considerations
&lt;/h3&gt;

&lt;p&gt;Your network setup plays a critical role in both performance and security. Here are some best practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use VLANs to segment your NAS traffic from other devices.&lt;/li&gt;
&lt;li&gt;Set up a dedicated management interface for TrueNAS.&lt;/li&gt;
&lt;li&gt;Enable jumbo frames if your network supports it for better performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Security Note:&lt;/strong&gt; Never expose your TrueNAS web interface directly to the internet. Always use a VPN or reverse proxy with authentication.&lt;/p&gt;

&lt;p&gt;For homelabs with multiple devices, consider using a managed switch to create VLANs (Virtual Local Area Networks). VLANs allow you to isolate your NAS from less secure devices, such as IoT gadgets, reducing the risk of lateral movement in case of a breach. For example, you could place your NAS on VLAN 10 and your IoT devices on VLAN 20, ensuring they can’t communicate directly.&lt;/p&gt;

&lt;p&gt;Another important aspect of network planning is IP addressing. Assign a static IP to your TrueNAS server to avoid issues with DHCP leases expiring or changing. This is especially important if you plan to access your NAS remotely or integrate it with other services like Proxmox or Plex.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation and Initial Configuration
&lt;/h2&gt;

&lt;p&gt;With your hardware and network plan in place, it’s time to install TrueNAS. Here’s a step-by-step guide:&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing TrueNAS
&lt;/h3&gt;

&lt;p&gt;Download the latest ISO from the official TrueNAS website. Use a tool like Rufus to create a bootable USB drive. Boot your server from the USB and follow the installation wizard. Choose the boot drive carefully—it should be a small SSD or USB stick, separate from your storage drives.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example: Creating a bootable USB on Linux&lt;/span&gt;
&lt;span class="nb"&gt;sudo dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;truenas.iso &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sdX &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4M &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During installation, you’ll be prompted to configure basic settings like timezone and network interfaces. Take your time to review these options, as they can impact your system’s performance and accessibility. For example, if you’re using multiple NICs, ensure the correct one is selected for management purposes.&lt;/p&gt;

&lt;p&gt;💡 Pro Tip: If you’re using a USB stick as your boot drive, consider creating a backup of the installation. USB drives can fail over time, so having a backup will save you from having to reinstall and reconfigure everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Storage Pools and Datasets
&lt;/h3&gt;

&lt;p&gt;Once installed, log in to the TrueNAS web interface. The first step is setting up your storage pool. Use RAID-Z for redundancy and performance. For example, RAID-Z2 offers a good balance of fault tolerance and usable space.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example: Creating a ZFS pool via CLI (if needed)&lt;/span&gt;
zpool create &lt;span class="nt"&gt;-f&lt;/span&gt; mypool raidz2 /dev/sd[b-e]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create datasets for organizing your data. Datasets allow you to apply specific settings like compression, quotas, and permissions at a granular level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💡 Pro Tip:&lt;/strong&gt; Enable compression (e.g., LZ4) on all datasets. It improves performance and saves space without noticeable overhead.&lt;/p&gt;

&lt;p&gt;When setting up datasets, think about how you’ll use your storage. For example, you might create separate datasets for media, backups, and personal files. This not only helps with organization but also allows you to apply different settings to each dataset. For instance, you could enable deduplication for backups but disable it for media files to save on system resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up User Accounts
&lt;/h3&gt;

&lt;p&gt;TrueNAS supports multiple user accounts, each with specific permissions. Avoid using the root account for daily tasks. Instead, create individual accounts for each user and assign them to groups for easier management.&lt;/p&gt;

&lt;p&gt;To enhance security, use strong, unique passwords for each account. If you’re managing multiple users, consider enabling two-factor authentication (2FA) for added protection. TrueNAS also supports SSH key-based authentication, which is more secure than password-based logins.&lt;/p&gt;

&lt;p&gt;💡 Pro Tip: Use groups to manage permissions more efficiently. For example, create a “Media” group for users who need access to your media dataset, and assign permissions at the group level instead of individually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Enterprise-Grade Security Practices
&lt;/h2&gt;

&lt;p&gt;Now that your TrueNAS is up and running, let’s secure it. These steps will help you implement enterprise-grade security practices:&lt;/p&gt;

&lt;h3&gt;
  
  
  Enabling Encryption
&lt;/h3&gt;

&lt;p&gt;TrueNAS supports encryption at the dataset level. Enable it during dataset creation and store the encryption keys securely. For added security, use a hardware security module (HSM) or a password-protected key file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example: Encrypting a dataset via CLI&lt;/span&gt;
zfs create &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;encryption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;on &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;keyformat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;passphrase mypool/securedata

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Encryption is a critical feature for protecting sensitive data, but it’s only effective if the keys are managed properly. Avoid storing encryption keys on the same device as your TrueNAS server. Instead, use a secure external device or a dedicated key management system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💡 Pro Tip:&lt;/strong&gt; Regularly back up your encryption keys and store them in a secure location. Losing your keys means losing access to your encrypted data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Firewalls and VPNs
&lt;/h3&gt;

&lt;p&gt;Use a firewall like OPNsense to restrict access to your TrueNAS server. Set up rules to allow only trusted IPs or VPN connections. For remote access, configure a VPN (e.g., WireGuard or OpenVPN) to securely tunnel into your network.&lt;/p&gt;

&lt;p&gt;When configuring your firewall, consider using geo-blocking to restrict access from countries you don’t expect traffic from. Additionally, enable logging to monitor access attempts and identify potential threats. For VPNs, WireGuard is a lightweight and modern option that offers excellent performance and security.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Security Note:&lt;/strong&gt; Avoid using outdated VPN protocols like PPTP, as they are no longer considered secure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Regular Updates and Patching
&lt;/h3&gt;

&lt;p&gt;Keeping your system updated is critical. TrueNAS provides a built-in updater for applying patches and updates. Schedule regular maintenance windows to ensure your system stays secure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Security Note:&lt;/strong&gt; Always test updates in a staging environment before applying them to production systems.&lt;/p&gt;

&lt;p&gt;Updates often include security patches that address newly discovered vulnerabilities. Delaying updates can leave your system exposed to attacks. If possible, enable email notifications for update availability so you’re always informed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintenance and Best Practices
&lt;/h2&gt;

&lt;p&gt;Maintaining your TrueNAS setup is just as important as the initial configuration. Here are some best practices:&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring System Health
&lt;/h3&gt;

&lt;p&gt;Enable email alerts to stay informed about system events. Use tools like Grafana and Prometheus to monitor metrics like disk usage, CPU load, and network traffic.&lt;/p&gt;

&lt;p&gt;Regularly check the SMART status of your drives to identify potential failures before they occur. TrueNAS includes built-in tools for monitoring drive health, but you can also use third-party solutions for more detailed insights.&lt;/p&gt;

&lt;p&gt;💡 Pro Tip: Set up a dashboard in Grafana to visualize key metrics at a glance. This makes it easier to identify trends and spot issues early.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automating Backups
&lt;/h3&gt;

&lt;p&gt;Set up automated snapshots and replication tasks to back up your data. Store backups offsite or in a separate location within your homelab.&lt;/p&gt;

&lt;p&gt;For critical data, consider using a 3-2-1 backup strategy: three copies of your data, stored on two different media types, with one copy offsite. This ensures you’re protected against hardware failures, accidental deletions, and disasters like fires or floods.&lt;/p&gt;

&lt;p&gt;💡 Pro Tip: Use cloud storage services like Backblaze B2 or Wasabi for offsite backups. TrueNAS supports integration with these services for seamless replication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Periodic Security Audits
&lt;/h3&gt;

&lt;p&gt;Review logs and access records regularly. Look for unusual activity and address potential vulnerabilities promptly.&lt;/p&gt;

&lt;p&gt;Security audits should include checking for unused accounts, outdated permissions, and unpatched vulnerabilities. Use tools like Nessus or OpenVAS to scan your network for potential issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling Up: Future-Proofing Your Homelab
&lt;/h2&gt;

&lt;p&gt;As your storage needs grow, you’ll need to scale your TrueNAS setup. Here’s how to prepare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add more drives to your pool or create additional pools for specific workloads.&lt;/li&gt;
&lt;li&gt;Integrate TrueNAS with other homelab services like Proxmox or Kubernetes.&lt;/li&gt;
&lt;li&gt;Stay informed about emerging security trends and adapt your setup accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scaling up often involves adding more hardware, which can introduce new challenges. For example, adding drives to an existing pool may require rebalancing data, which can be time-consuming. Plan for these scenarios in advance to minimize downtime.&lt;/p&gt;

&lt;p&gt;💡 Pro Tip: Use hot-swappable drive bays for easier hardware upgrades. This allows you to replace or add drives without shutting down your server.&lt;/p&gt;

&lt;h2&gt;
  
  
  New Section: Integrating TrueNAS with Other Services
&lt;/h2&gt;

&lt;p&gt;TrueNAS can be integrated with a variety of services to enhance its functionality. Here are some popular integrations:&lt;/p&gt;

&lt;h3&gt;
  
  
  Media Servers
&lt;/h3&gt;

&lt;p&gt;TrueNAS works seamlessly with media servers like Plex and Emby. Store your media files on a dedicated dataset and configure your media server to access them. This setup allows you to stream movies, TV shows, and music directly from your NAS.&lt;/p&gt;

&lt;p&gt;💡 Pro Tip: Use SSDs for your media dataset if you frequently access large files. This improves performance and reduces buffering.&lt;/p&gt;

&lt;h3&gt;
  
  
  Virtualization Platforms
&lt;/h3&gt;

&lt;p&gt;If you’re running a virtualization platform like Proxmox or VMware, you can use TrueNAS as a shared storage solution. Configure iSCSI or NFS shares to provide high-performance storage for your virtual machines.&lt;/p&gt;

&lt;p&gt;💡 Pro Tip: Use separate datasets for each VM to simplify management and improve performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  New Section: Advanced Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Even with the best planning, issues can arise. Here’s how to troubleshoot common problems:&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Issues
&lt;/h3&gt;

&lt;p&gt;If your TrueNAS server is running slowly, check the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disk health: Use SMART tools to identify failing drives.&lt;/li&gt;
&lt;li&gt;Network configuration: Ensure your NICs are configured correctly and aren’t overloaded.&lt;/li&gt;
&lt;li&gt;Resource usage: Monitor CPU and RAM usage to identify bottlenecks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 Pro Tip: Use the built-in reporting tools in TrueNAS to visualize performance metrics over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access Problems
&lt;/h3&gt;

&lt;p&gt;If users can’t access their data, check the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Permissions: Ensure the correct permissions are set on datasets and shares.&lt;/li&gt;
&lt;li&gt;Network connectivity: Verify that the server is reachable and the correct IP is being used.&lt;/li&gt;
&lt;li&gt;Authentication: Check user accounts and passwords for errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What’s the difference between TrueNAS CORE and SCALE?
&lt;/h3&gt;

&lt;p&gt;CORE is FreeBSD-based and ideal for traditional NAS use. SCALE is Linux-based and supports containers and VMs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use consumer-grade hardware for TrueNAS?
&lt;/h3&gt;

&lt;p&gt;You can, but enterprise-grade hardware (e.g., ECC RAM, server-grade drives) is recommended for reliability and data integrity.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I secure remote access to TrueNAS?
&lt;/h3&gt;

&lt;p&gt;Use a VPN like WireGuard or OpenVPN. Avoid exposing the TrueNAS web interface directly to the internet.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s the best way to back up TrueNAS data?
&lt;/h3&gt;

&lt;p&gt;Use ZFS snapshots and replication tasks. Store backups offsite or on a separate server for redundancy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🛠️ Recommended Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tools and books mentioned in (or relevant to) this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/dp/B08C4Z69LN?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;Crucial 64GB DDR4 ECC SODIMM Kit&lt;/a&gt; — ECC RAM for data integrity in your NAS or hypervisor ($150-200)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/dp/B0CMQ6SK7W?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;WD Red Plus 8TB NAS HDD&lt;/a&gt; — CMR drive designed for 24/7 NAS operation with RAID support ($140-180)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/dp/B0DCVJ8XTR?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;Beelink EQR6 Mini PC (Ryzen 7 6800U)&lt;/a&gt; — Compact powerhouse for Proxmox or TrueNAS virtualization ($350-500)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/dp/B000FBK3QK?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;APC UPS 1500VA&lt;/a&gt; — Battery backup to protect your homelab from power outages ($170-200)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TrueNAS offers enterprise-grade features for homelabs, but proper configuration is essential for security.&lt;/li&gt;
&lt;li&gt;Use ECC memory, RAID-Z, and VLANs to ensure data integrity and network segmentation.&lt;/li&gt;
&lt;li&gt;Enable encryption, configure firewalls, and use VPNs for secure access.&lt;/li&gt;
&lt;li&gt;Regular updates, backups, and security audits are non-negotiable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.truenas.com/" rel="noopener noreferrer"&gt;TrueNAS Official Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.zfsbuild.com/" rel="noopener noreferrer"&gt;ZFS Build Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opnsense.org/" rel="noopener noreferrer"&gt;OPNsense Firewall&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wireguard.com/" rel="noopener noreferrer"&gt;WireGuard VPN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://orthogonal.info/enterprise-security-at-home-wazuh-suricata-setup/" rel="noopener noreferrer"&gt;Enterprise Security at Home: Wazuh &amp;amp; Suricata Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://orthogonal.info/secure-truenas-plex-setup-homelab/" rel="noopener noreferrer"&gt;Secure TrueNAS Plex Setup for Your Homelab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://orthogonal.info/stop-ngrok-tunnels-enterprise-security-homelab/" rel="noopener noreferrer"&gt;Stop Ngrok Tunnels: Enterprise Security at Home&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;&lt;a href="https://orthogonal.info/truenas-setup-guide-enterprise-security-at-home/" rel="noopener noreferrer"&gt;Read the full article on orthogonal.info&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>homelab</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>Track Congressional Stock Trades with Python and Free SEC Data</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Mon, 13 Apr 2026 18:12:52 +0000</pubDate>
      <link>https://dev.to/orthogonalinfo/track-congressional-stock-trades-with-python-and-free-sec-data-309d</link>
      <guid>https://dev.to/orthogonalinfo/track-congressional-stock-trades-with-python-and-free-sec-data-309d</guid>
      <description>&lt;p&gt;Last month I noticed something odd: a senator sold $2M in hotel stocks three days before a travel industry report tanked the sector. Coincidence? Maybe. But it got me wondering — is there an easy way to track what members of Congress are buying and selling?&lt;/p&gt;

&lt;p&gt;Turns out, the STOCK Act of 2012 requires all members of Congress to disclose securities transactions within 45 days. These filings are public. And you can pull them programmatically. I built a Python script that checks for new congressional trades daily, flags the interesting ones, and sends me alerts. Here’s exactly how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Congressional Trades Matter
&lt;/h2&gt;

&lt;p&gt;Members of Congress sit on committees that regulate industries, receive classified briefings, and vote on bills that move markets. Whether they’re trading on insider knowledge is a debate I’ll leave to lawyers. What I care about is this: as a group, congressional traders have historically outperformed the S&amp;amp;P 500 by 6-12% annually, depending on the study you reference. A 2022 paper from the University of Georgia put the figure at 8.9% annualized excess returns for Senate trades.&lt;/p&gt;

&lt;p&gt;Even if you think it’s all luck, following these trades is a free signal you can add to your research process. At worst, it shows you where politically-connected money is flowing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the Data Lives
&lt;/h2&gt;

&lt;p&gt;Congressional financial disclosures are filed through two systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Senate&lt;/strong&gt;: efdsearch.senate.gov — the Electronic Financial Disclosures database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;House&lt;/strong&gt;: disclosures-clerk.house.gov — the Clerk of the House system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are publicly searchable, but neither offers a clean API. The Senate site has a search form that returns HTML results. The House site recently added a JSON search endpoint, which is nicer to work with. Several community projects scrape and normalize this data — the most maintained one is the &lt;a href="https://house-stock-watcher-data.s3-us-west-2.amazonaws.com/data/all_transactions.json" rel="noopener noreferrer"&gt;House Stock Watcher dataset&lt;/a&gt; on S3, which gets updated daily.&lt;/p&gt;

&lt;p&gt;For this project, I combined the House Stock Watcher dataset (free, updated daily, clean JSON) with direct scraping of the Senate EFD search for the freshest possible data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Python Script
&lt;/h2&gt;

&lt;p&gt;Here’s the core of what I run. It pulls House transactions from the public S3 dataset, filters for trades above $15,000 (the minimum reporting threshold is $1,001, but small trades are noise), and flags any trades in the last 7 days:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;

&lt;span class="n"&gt;HOUSE_DATA_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://house-stock-watcher-data.s3-us-west-2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.amazonaws.com/data/all_transactions.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_house_trades&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days_back&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$15,001 - $50,000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HOUSE_DATA_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;trades&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="n"&gt;cutoff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;days_back&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;amount_tiers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$15,001 - $50,000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$50,001 - $100,000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$100,001 - $250,000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$250,001 - $500,000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$500,001 - $1,000,000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$1,000,001 - $5,000,000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$5,000,001 - $25,000,000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$25,000,001 - $50,000,000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;tier_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;amount_tiers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;valid_tiers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount_tiers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;tier_idx&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;

    &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;tx_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;transaction_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%Y-%m-%d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tx_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;cutoff&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;valid_tiers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;recent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;recent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;transaction_date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each transaction record includes the representative’s name, ticker, transaction type (purchase/sale), amount range, and disclosure date. The amount ranges are annoying — Congress doesn’t disclose exact figures, just brackets — but even the brackets tell you a lot when someone drops $500K+ on a single stock.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filtering for Signal
&lt;/h2&gt;

&lt;p&gt;Raw congressional trade data is noisy. Most trades are mutual fund purchases or routine portfolio rebalancing. The interesting stuff is when you see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Committee-relevant trades&lt;/strong&gt; — A member of the Armed Services Committee buying defense stocks, or a Finance Committee member trading bank shares&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cluster buys&lt;/strong&gt; — Multiple members buying the same ticker within a short window&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Large single-stock positions&lt;/strong&gt; — Anything above $250K in one company&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timing around legislation&lt;/strong&gt; — Trades made shortly before committee votes or bill introductions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I added a scoring function that flags trades matching these patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;COMMITTEE_SECTORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Armed Services&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;LMT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RTX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NOC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Energy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;XOM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CVX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;COP&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SLB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EOG&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Finance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JPM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BAC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;C&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Health&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UNH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JNJ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PFE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABBV&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MRK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Technology&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AAPL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MSFT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GOOGL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AMZN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;META&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;score_trade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;member_committees&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Large position = more interesting
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$250,001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$500,001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$1,000,001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;

    &lt;span class="c1"&gt;# Committee relevance
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;committee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tickers&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;COMMITTEE_SECTORS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;committee&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;member_committees&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;ticker&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tickers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="c1"&gt;# Purchase vs sale (purchases are more actionable)
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;purchase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The committee mapping is simplified here — in production I maintain a fuller list pulled from congress.gov. But even this basic version catches the most egregious cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Daily Alerts
&lt;/h2&gt;

&lt;p&gt;I run this on a &lt;a href="https://www.amazon.com/Raspberry-Model-2GB-Quad-Core-Bluetooth/dp/B09VKJ1KC5?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;Raspberry Pi 4&lt;/a&gt; (affiliate link) sitting in my closet. A cron job runs the script every morning at 7 AM, checks for new trades filed since the last run, and sends me a notification via ntfy (a free, self-hosted push notification tool).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;congress-trades&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://ntfy.sh/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Congressional Trade Alert&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# In main loop:
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;trade&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;fetch_house_trades&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;days_back&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$50,001 - $100,000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;representative&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ticker&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;send_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Raspberry Pi draws about 5 watts, costs nothing to run, and handles this job without breaking a sweat. If you don’t want to run your own hardware, a $5/month VPS from any provider works too. I wrote about &lt;a href="https://orthogonal.info/secure-truenas-plex-homelab/" rel="noopener noreferrer"&gt;setting up a homelab for projects like this&lt;/a&gt; if you want to go the self-hosted route.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I’ve Learned Running This for 6 Months
&lt;/h2&gt;

&lt;p&gt;A few patterns jumped out after collecting data since late 2025:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclosure delays are the real problem.&lt;/strong&gt; The 45-day filing window means by the time you see a trade, the move may already be priced in. The most useful trades are the ones filed quickly — within 10-15 days. Some members consistently file within a week; those are the ones I weight highest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cluster signals beat individual trades.&lt;/strong&gt; One senator buying Nvidia means nothing. Three members from different parties all buying Nvidia in the same two-week window? That’s worth investigating. My script tracks cluster buys — 3+ distinct members trading the same ticker within 14 days — and those have been the most actionable signals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sales matter more than purchases for timing.&lt;/strong&gt; Purchases can be routine investment. But when several members suddenly sell the same sector? That’s been a leading indicator for bad news more often than purchases predict good news.&lt;/p&gt;

&lt;p&gt;I won’t claim this is a trading strategy on its own — it’s one data point I check alongside technicals, fundamentals, and &lt;a href="https://orthogonal.info/insider-trading-detector-python-sec-data/" rel="noopener noreferrer"&gt;corporate insider trades from SEC Form 4 filings&lt;/a&gt;. The congressional data adds a political risk dimension that most retail traders ignore entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives and Paid Tools
&lt;/h2&gt;

&lt;p&gt;If you don’t want to build your own, several paid services track this data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quiver Quantitative&lt;/strong&gt; (free tier + paid) — best visualization, shows committee-trade correlations. The free tier covers delayed data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capitol Trades&lt;/strong&gt; (free) — clean interface, basic filtering. No alerts or scoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unusual Whales&lt;/strong&gt; ($30-100/mo) — includes congressional data alongside options flow. Worth it if you want both in one platform.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I prefer my DIY version because I can customize the scoring, add my own committee mappings, and cross-reference against other datasets I already collect. But if you just want to glance at the data without writing code, Capitol Trades is solid and free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending It
&lt;/h2&gt;

&lt;p&gt;The basic script above gets you 80% of the value. If you want to go further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add Senate data&lt;/strong&gt; — the EFD search site requires a bit more scraping work since it returns HTML, but BeautifulSoup handles it. A good &lt;a href="https://www.amazon.com/Web-Scraping-Python-Collecting-Modern/dp/1098145348?tag=orthogonalinf-20" rel="noopener noreferrer"&gt;Python web scraping reference&lt;/a&gt; (affiliate link) will save you hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-reference with Polygon.io&lt;/strong&gt; — I use &lt;a href="https://orthogonal.info/pre-ipo-api-sec-filings-spacs-lockup-data/" rel="noopener noreferrer"&gt;Polygon’s market data API&lt;/a&gt; to check price action after each disclosed trade. This lets you backtest whether following congressional trades would have been profitable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build a dashboard&lt;/strong&gt; — Grafana + SQLite gives you a clean visual history. Run it on the same Pi.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Track state-level trades&lt;/strong&gt; — Some states have their own disclosure requirements for governors and state legislators. Less data, but less competition from other trackers too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full source code for my version is about 400 lines of Python with zero paid dependencies — just stdlib plus BeautifulSoup for the Senate scraping. I might open-source it if there’s interest; drop a comment below if that’d be useful.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I publish daily market intelligence — including congressional trade alerts — on our free Telegram channel. Join &lt;a href="https://t.me/alphasignal822" rel="noopener noreferrer"&gt;Alpha Signal&lt;/a&gt; for daily signals, trade analysis, and macro context. No fluff, no paywalls on the basics.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>CSS Gradient Builder: Fixing Annoyances of Existing Tools</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Mon, 13 Apr 2026 16:47:44 +0000</pubDate>
      <link>https://dev.to/orthogonalinfo/css-gradient-builder-fixing-annoyances-of-existing-tools-1lh6</link>
      <guid>https://dev.to/orthogonalinfo/css-gradient-builder-fixing-annoyances-of-existing-tools-1lh6</guid>
      <description>&lt;p&gt;Last Tuesday I needed a conic gradient. Not a linear one, not a radial one — specifically a conic gradient for a loading spinner I was building. I opened three different gradient generators. None of them supported conic gradients. The ones that did were buried under ads, tracking scripts, and cookie consent banners that took longer to dismiss than the actual gradient took to build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; GradientForge is a free browser-based CSS gradient builder that fixes the annoyances of existing tools — it supports linear, radial, and conic gradients, provides real-time preview with no ads or tracking, outputs clean CSS with vendor prefixes, and includes preset collections for common design patterns.&lt;/p&gt;

&lt;p&gt;So I spent my afternoon building GradientForge instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem With Existing Gradient Tools
&lt;/h2&gt;

&lt;p&gt;I tested the three most popular gradient generators before writing a single line of code. Here's what I found:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;cssgradient.io&lt;/strong&gt; is the go-to recommendation on Stack Overflow answers from 2019. It handles linear and radial gradients well enough, but it's slow. The page loads with trackers, analytics, and display ads competing for bandwidth. When I tested on a throttled 3G connection, first meaningful paint took over four seconds. For a tool that should generate a CSS property in under a second, that's unacceptable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grabient&lt;/strong&gt; looks beautiful — I'll give it that. But it's primarily a preset gallery with limited customization. Want to add a third color stop? That's buried in the interface. Want conic gradients? Not available. Want to export as SVG for a design file? Nope.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;uiGradients&lt;/strong&gt; follows the same preset-only pattern. Pick from a curated list, copy the CSS. No custom stop positions, no angle fine-tuning, no easing control. It's a gradient menu, not a gradient builder.&lt;/p&gt;

&lt;p&gt;Every single one of these tools was missing at least one thing I needed: conic gradient support, easing between color stops, SVG export, or just basic speed. I wanted all of those in one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  What GradientForge Actually Does
&lt;/h2&gt;

&lt;p&gt;GradientForge supports all three CSS gradient types: linear, radial, and conic. You pick your type, adjust the parameters, and see the result update in real-time on a full-screen canvas preview. The CSS code appears below, ready to copy with one click or keyboard shortcut (Ctrl+C when nothing is selected).&lt;/p&gt;

&lt;p&gt;The color stop system works the way it should. Click a color picker, drag the position handle along the gradient bar, or type an exact percentage. Double-click the bar to add a new stop at that position — the tool interpolates the correct color automatically. You can have up to 10 stops per gradient, which covers every practical use case I've encountered.&lt;/p&gt;

&lt;p&gt;The feature I'm most proud of is the &lt;strong&gt;easing system&lt;/strong&gt;. Standard CSS gradients transition linearly between color stops, which often produces muddy middle zones where colors mix in ugly ways. GradientForge generates additional intermediate stops that follow an easing curve — ease-in, ease-out, ease-in-out, or stepped transitions. The result is smoother, more visually pleasing gradients without manual fine-tuning of each stop position.&lt;/p&gt;

&lt;p&gt;Here's what happens technically: when you select an easing function, the tool interpolates 8 additional color stops between each pair of your original stops, positioning them along the chosen easing curve. The browser sees a gradient with many stops, but the transitions follow a cubic or stepped curve instead of a linear one. The output CSS is longer, but the visual result is noticeably better, especially for gradients spanning complementary colors.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;GradientForge is a single HTML file with inline CSS and JavaScript — zero dependencies. No React, no Tailwind, no build step, no node_modules. The entire tool is about 36KB — smaller than most hero images. It loads in under 100ms on any modern connection.&lt;/p&gt;

&lt;p&gt;The architecture is straightforward state management. A single JavaScript object holds the current gradient configuration: type, angle, color stops, easing mode, and type-specific parameters. Every time any control changes, the entire UI re-renders from that state object. It sounds wasteful, but with only a few DOM elements to update, each render cycle takes under 2ms.&lt;/p&gt;

&lt;p&gt;The color stop bar uses pointer events for drag handling. Each stop is a positioned &lt;code&gt;div&lt;/code&gt; inside the bar container. On mousedown, I capture the element, switch to mousemove tracking on the document (not the bar — that prevents losing the drag when the cursor moves fast), and compute the percentage position from the cursor's X coordinate relative to the bar's bounding rect. Touch events follow the same pattern for mobile support.&lt;/p&gt;

&lt;p&gt;For color interpolation, I convert hex colors to RGB components, interpolate each channel independently, and convert back. This happens in sRGB space, which isn't perceptually uniform — I'd like to add OKLCH interpolation in a future version for even smoother results. But for most practical gradients, sRGB interpolation is indistinguishable from perceptual to human eyes.&lt;/p&gt;

&lt;p&gt;The SVG export translates CSS gradient parameters into SVG gradient elements. Linear gradients map directly to &lt;code&gt;&amp;lt;linearGradient&amp;gt;&lt;/code&gt; with computed x1/y1/x2/y2 coordinates derived from the CSS angle. Radial gradients use &lt;code&gt;&amp;lt;radialGradient&amp;gt;&lt;/code&gt; with center positions. Conic gradients don't have a native SVG equivalent, so the tool falls back to a linear approximation — not perfect, but useful enough for most design workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  The URL State Trick
&lt;/h2&gt;

&lt;p&gt;Every gradient configuration is encoded in the URL query parameters. Change a color, move a stop, switch the type — the URL updates silently via &lt;code&gt;history.replaceState&lt;/code&gt;. This means you can share a gradient by sharing the URL. No accounts, no saving to a database, no server-side state. The recipient opens the link and sees your exact gradient configuration ready to use.&lt;/p&gt;

&lt;p&gt;The encoding is compact: gradient type is a single character (l/r/c), stops are comma-separated hex:position pairs, and type-specific parameters use short keys. A three-stop linear gradient with easing encodes to about 120 characters in the URL — short enough to paste in a Slack message without it looking intimidating.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy and Performance
&lt;/h2&gt;

&lt;p&gt;Everything runs in your browser. There's no server processing, no analytics tracking your color choices, no data leaving your machine. The tool works completely offline once loaded — I included a service worker that caches all assets. Install it as a PWA and you've got a native-feeling gradient builder that works on a plane.&lt;/p&gt;

&lt;p&gt;I ran Lighthouse on the deployed version: 100 across all four categories. Performance, accessibility, best practices, SEO — all perfect scores. That's what happens when your entire app is 36KB of self-contained HTML with proper ARIA labels and semantic markup.&lt;/p&gt;

&lt;h2&gt;
  
  
  12 Built-In Presets
&lt;/h2&gt;

&lt;p&gt;Sometimes you don't want to build from scratch. GradientForge includes 12 presets — Sunset, Ocean, Forest, Flame, Night, Peach, Arctic, Berry, Candy, Mint, Dusk, and Neon. Click one to load it, then customize from there. They're starting points, not endpoints.&lt;/p&gt;

&lt;p&gt;The presets also serve as a discovery tool. If you're not sure what conic gradients look like, hit the Random button. It generates a random type, random angle, random colors, and random number of stops. Hit it ten times and you'll have a better intuition for what each gradient type does than reading any tutorial could give you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dark Mode and Mobile
&lt;/h2&gt;

&lt;p&gt;The interface respects your system color scheme preference automatically. No toggle needed — though I might add one in a future update for users who want to test their gradient against both backgrounds. On mobile, the layout shifts from a side-by-side view (preview + controls) to a stacked view with the preview on top and controls scrollable below. Touch targets are 44px minimum for comfortable thumb navigation.&lt;/p&gt;

&lt;p&gt;I tested at 320px width (iPhone SE), 768px (iPad), and 1440px (desktop). The gradient preview always takes up as much space as possible — that's the point of the tool, after all. Controls compress but remain usable at every breakpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I have a short list of features I want to add: OKLCH color interpolation for perceptually smoother gradients, a gradient animation builder (because CSS can animate gradient positions), multi-gradient layering (stack multiple gradients on one element), and an accessibility checker that warns when your gradient doesn't meet contrast requirements for text overlays.&lt;/p&gt;

&lt;p&gt;For now, GradientForge does exactly what I needed: build any CSS gradient, with any number of stops, with smooth easing, in any of the three gradient types, and copy the result in one click. No ads, no tracking, no signup. Just gradients.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://gradientforge.orthogonal.info/" rel="noopener noreferrer"&gt;Try GradientForge →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does GradientForge support conic gradients?
&lt;/h3&gt;

&lt;p&gt;Yes — this was the primary motivation for building it. Most existing CSS gradient generators only support linear and radial gradients. GradientForge supports all three types: linear, radial, and conic, with full control over angle, color stops, and positioning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is GradientForge free to use?
&lt;/h3&gt;

&lt;p&gt;Completely free, with no ads, tracking scripts, or account requirements. It runs entirely in your browser — no data is sent to any server.&lt;/p&gt;

&lt;h3&gt;
  
  
  What browsers support conic gradients?
&lt;/h3&gt;

&lt;p&gt;All modern browsers support conic gradients: Chrome 69+, Firefox 83+, Safari 12.1+, and Edge 79+. GradientForge includes vendor prefix options for broader compatibility and shows a browser support indicator for each gradient type you create.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MDN Web Docs. "&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/conic-gradient" rel="noopener noreferrer"&gt;CSS conic-gradient()&lt;/a&gt;." &lt;em&gt;Mozilla&lt;/em&gt;, 2026.&lt;/li&gt;
&lt;li&gt;Can I Use. "&lt;a href="https://caniuse.com/css-conic-gradients" rel="noopener noreferrer"&gt;CSS Conic Gradients browser support&lt;/a&gt;." &lt;em&gt;Can I Use&lt;/em&gt;, 2026.&lt;/li&gt;
&lt;li&gt;W3C. "&lt;a href="https://www.w3.org/TR/css-images-4/" rel="noopener noreferrer"&gt;CSS Images Module Level 4&lt;/a&gt;." &lt;em&gt;W3C&lt;/em&gt;, 2024.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>devtools</category>
      <category>javascript</category>
    </item>
    <item>
      <title>OpenClaw Setup: Zero to Autonomous AI Mastery</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Sun, 12 Apr 2026 16:49:30 +0000</pubDate>
      <link>https://dev.to/orthogonalinfo/openclaw-setup-zero-to-autonomous-ai-mastery-4374</link>
      <guid>https://dev.to/orthogonalinfo/openclaw-setup-zero-to-autonomous-ai-mastery-4374</guid>
      <description>&lt;p&gt;Setting up OpenClaw is easy. Setting it up &lt;em&gt;right&lt;/em&gt; so your AI agent actually does useful work autonomously takes some know-how.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes OpenClaw Different
&lt;/h2&gt;

&lt;p&gt;Unlike ChatGPT or Claude, which respond to individual prompts, OpenClaw creates &lt;strong&gt;persistent AI agents&lt;/strong&gt; that remember between sessions, act autonomously through cron jobs, use real tools like browser automation and APIs, and self-improve by editing their own configuration.&lt;/p&gt;

&lt;p&gt;With Hostinger now offering 1-click OpenClaw deployment, the barrier to entry has never been lower. But the gap between &lt;em&gt;installed&lt;/em&gt; and &lt;em&gt;productive&lt;/em&gt; is where most people get stuck.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 Mistakes New OpenClaw Users Make
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Generic SOUL.md
&lt;/h3&gt;

&lt;p&gt;Your SOUL.md file is your agent's personality and decision-making framework. A generic "you are a helpful assistant" produces generic results. A well-crafted SOUL.md with specific principles, boundaries, and communication style creates an agent that feels like a capable teammate.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. No Memory Protocol
&lt;/h3&gt;

&lt;p&gt;Without structured memory, every session starts from scratch. The 3-layer memory system (State, Journal, Knowledge) gives your agent continuity. It remembers what worked, what failed, and what it learned — across sessions and days.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Manual-Only Operation
&lt;/h3&gt;

&lt;p&gt;The real power of OpenClaw is autonomous operation via cron jobs. An agent that only responds to messages is using 10% of its potential. Cron jobs let your agent monitor, create, publish, and optimize while you sleep.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start Checklist
&lt;/h2&gt;

&lt;p&gt;If you're setting up OpenClaw for the first time, here's what actually matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Edit SOUL.md&lt;/strong&gt; — give your agent a specific personality and principles, not generic instructions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edit USER.md&lt;/strong&gt; — tell it who you are and what you need done&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edit TOOLS.md&lt;/strong&gt; — add your local services (Home Assistant, NAS, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create the data/ directory&lt;/strong&gt; with &lt;code&gt;state.json&lt;/code&gt;, &lt;code&gt;knowledge.md&lt;/code&gt;, and &lt;code&gt;journal/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up 3 starter crons&lt;/strong&gt;: email check (every 2h), weather (morning), RSS monitor (every 4h)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure browser-agent&lt;/strong&gt; for web automation tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test a heartbeat cycle&lt;/strong&gt; to verify autonomous operation works end-to-end&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create HEARTBEAT.md&lt;/strong&gt; with your periodic task checklist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference between a toy setup and a useful one is usually about 30 minutes of configuration. The memory protocol alone transforms the experience from "stateless chatbot" to "AI that actually knows what happened yesterday."&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned Running This in Production
&lt;/h2&gt;

&lt;p&gt;I've been running an OpenClaw agent with 31 skills and 25+ daily cron jobs since March 2026. A few things surprised me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory matters more than model choice.&lt;/strong&gt; Switching from GPT-4o to Claude Opus made less difference than implementing proper state persistence. An agent that remembers its failures and learns from them outperforms a smarter agent that starts fresh every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cron patterns need iteration.&lt;/strong&gt; My first cron jobs ran too frequently and produced noise. The sweet spot was fewer jobs that batch related checks together. One heartbeat that checks email + calendar + weather beats three separate crons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skills compound.&lt;/strong&gt; Each new skill makes existing ones more useful. Browser automation + RSS monitoring = automatic content research. Trading data + newsletter publishing = daily market intelligence on autopilot.&lt;/p&gt;

&lt;p&gt;The full setup guide with templates, 30 production-tested cron patterns, and SOUL.md examples is available at &lt;a href="https://orthogonal.info/openclaw-setup-guide-mastery-pack/" rel="noopener noreferrer"&gt;orthogonal.info&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions about OpenClaw setup? Drop them in the comments — I've probably hit the same wall.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>automation</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
