<?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: D_SECURITY</title>
    <description>The latest articles on DEV Community by D_SECURITY (@d_security).</description>
    <link>https://dev.to/d_security</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%2F3895597%2F93c8a854-5160-40ca-bab5-d8a8f2d5b7bb.png</url>
      <title>DEV Community: D_SECURITY</title>
      <link>https://dev.to/d_security</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/d_security"/>
    <language>en</language>
    <item>
      <title>Building a zero-infra job queue with SQLite (and stress-testing it to 14 jobs/sec)</title>
      <dc:creator>D_SECURITY</dc:creator>
      <pubDate>Sun, 24 May 2026 17:56:14 +0000</pubDate>
      <link>https://dev.to/d_security/building-a-zero-infra-job-queue-with-sqlite-and-stress-testing-it-to-14-jobssec-cb0</link>
      <guid>https://dev.to/d_security/building-a-zero-infra-job-queue-with-sqlite-and-stress-testing-it-to-14-jobssec-cb0</guid>
      <description>&lt;p&gt;I needed to trigger automation scripts on an old Android phone from a cloud VPS, without opening ports or spinning up Redis. So I built a job queue out of Flask and SQLite.&lt;/p&gt;

&lt;p&gt;Here is a deep dive into what it is, how SQLite handles the concurrency, and what happened when I tried to stress-test it in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is (and what it isn't)
&lt;/h2&gt;

&lt;p&gt;Intent Bus is designed to be the missing link between simple &lt;code&gt;cron&lt;/code&gt; jobs and heavy enterprise message brokers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it aims for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Giving indie hackers and home-lab enthusiasts actual execution guarantees (retries, dead-letter queues) without needing to deploy or maintain broker infrastructure.&lt;/li&gt;
&lt;li&gt;Running anywhere. You can deploy the server on a free cloud tier, and run workers on a Raspberry Pi, a Termux phone, or a cheap VPS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What it is not:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is not a Kafka replacement. It is not meant for microservices processing 10,000 transactions per second. If you have enterprise scale, use enterprise tools. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Core Features
&lt;/h2&gt;

&lt;p&gt;Cron is strictly fire-and-forget, with no coordination and silent failures. To fix that, Intent Bus provides:&lt;/p&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;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Atomic Locking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prevents double-claiming. A cryptographic lease token is issued to the claiming worker.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reliable Delivery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;If a worker crashes or drops offline, the lease expires and the job requeues with exponential backoff.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dead-Letter Queue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Failed jobs (e.g., after 3 attempts) are archived for inspection rather than deleted.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Capability Routing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Workers advertise what they can do; jobs require what they need (e.g., require a worker with &lt;code&gt;ffmpeg&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Priority Queues&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High-priority intents are always claimed and processed first.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The SQLite Atomic Lock
&lt;/h2&gt;

&lt;p&gt;The biggest challenge with using SQLite for a queue is concurrent writes. If 40 workers poll the server at the exact same millisecond, they can't all claim the same job.&lt;/p&gt;

&lt;p&gt;Intent Bus solves this by strictly enforcing SQLite's &lt;code&gt;WAL&lt;/code&gt; (Write-Ahead Logging) mode and relying on the &lt;code&gt;UPDATE ... RETURNING&lt;/code&gt; clause introduced in SQLite 3.35.0. &lt;/p&gt;

&lt;p&gt;When a worker asks for a job, the server executes a single atomic transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;intents&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'claimed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;claimed_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;claim_expires_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;lease_exp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;claim_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;claim_attempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;claim_attempts&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;intents&lt;/span&gt; 
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'open'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;run_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
    &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
    &lt;span class="k"&gt;LIMIT&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;RETURNING&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;claim_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because SQLite serializes writes, this guarantees that only one worker will ever receive the ephemeral &lt;code&gt;claim_token&lt;/code&gt; required to fulfill or fail that specific job.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stress Test: A Tale of Two Deployments
&lt;/h2&gt;

&lt;p&gt;I wanted to see exactly where SQLite would choke under concurrent worker load, so I wrote an enterprise-style stress harness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt 1: The PythonAnywhere Disaster&lt;/strong&gt;&lt;br&gt;
I originally deployed the architecture to PythonAnywhere's free tier. The results were catastrophic. &lt;/p&gt;

&lt;p&gt;When I launched the high-intensity concurrent test, it didn't even manage to start. I stepped it down to the medium-intensity test (15 concurrent workers fighting for 500 jobs), and it threw &lt;strong&gt;100% network errors&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Why? Because PythonAnywhere's free tier routes requests through a single-threaded Gunicorn worker. The concurrent polling requests bottlenecked at the WSGI layer, queued up, and timed out before SQLite even knew what hit it. It wasn't a database lock issue; it was a server concurrency issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt 2: Docker on Render&lt;/strong&gt;&lt;br&gt;
I moved the exact same SQLite file and Flask app to a basic Docker container on Render, configured with multiple application threads (&lt;code&gt;--threads 4&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;Render's free tier aggressively puts your container to sleep when idle, meaning the very first request takes about 30 seconds to wake the server up (the dreaded cold start). But once it's awake? It handles the load beautifully.&lt;/p&gt;

&lt;p&gt;Here is the benchmark data from the Render Docker deployment:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;Workers&lt;/th&gt;
&lt;th&gt;Jobs&lt;/th&gt;
&lt;th&gt;Success&lt;/th&gt;
&lt;th&gt;P99 Latency&lt;/th&gt;
&lt;th&gt;Throughput&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Light&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;0.594s&lt;/td&gt;
&lt;td&gt;3.72 j/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Medium&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;98.75%&lt;/td&gt;
&lt;td&gt;0.517s&lt;/td&gt;
&lt;td&gt;13.27 j/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Heavy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;2000&lt;/td&gt;
&lt;td&gt;99.01%&lt;/td&gt;
&lt;td&gt;2.586s&lt;/td&gt;
&lt;td&gt;13.62 j/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Even with 40 concurrent workers furiously hammering the single SQLite file for write locks, there were &lt;strong&gt;0 network errors, 0 lost leases, and 0 rate limit crashes&lt;/strong&gt;. It comfortably sustained over 13 jobs per second. &lt;/p&gt;

&lt;h2&gt;
  
  
  Limits &amp;amp; The Theoretical Future
&lt;/h2&gt;

&lt;p&gt;Right now, the system is tuned for safety. Payloads are strictly limited to 8KB to prevent database bloat, and &lt;code&gt;busy_timeout&lt;/code&gt; PRAGMAs are set to allow workers to wait gracefully for locks. &lt;/p&gt;

&lt;p&gt;Could it go further? In theory, yes. If you moved off a free cloud container onto a dedicated VPS with NVMe storage, and tuned SQLite for pure speed (e.g., &lt;code&gt;PRAGMA synchronous=OFF&lt;/code&gt;), you could probably push 50-100 jobs a second. Furthermore, the architecture is simple enough that you could easily swap the &lt;code&gt;get_db()&lt;/code&gt; call to point to PostgreSQL if you needed horizontal scaling. &lt;/p&gt;

&lt;p&gt;But honestly, if you hit that scale, you &lt;em&gt;should&lt;/em&gt; just bite the bullet and deploy Redis. The entire philosophy of Intent Bus is avoiding that infrastructure jump until it is absolutely necessary. &lt;/p&gt;

&lt;p&gt;The server runs on Docker, Raspberry Pi, or any free cloud tier. Workers run anywhere that speaks HTTP — including Termux.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core Server:&lt;/strong&gt; &lt;a href="https://github.com/dsecurity49/Intent-Bus" rel="noopener noreferrer"&gt;GitHub - Intent Bus&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python SDK:&lt;/strong&gt; &lt;a href="https://github.com/dsecurity49/Intent-Bus-sdk" rel="noopener noreferrer"&gt;GitHub - Intent Bus SDK&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would love to hear your feedback. Has anyone else experimented with using SQLite for job queueing? What edge cases did you run into?&lt;/p&gt;

</description>
      <category>python</category>
      <category>sqlite</category>
      <category>architecture</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How I Coordinate Scripts Across Devices Without Open Ports, Firebase, or a VPS</title>
      <dc:creator>D_SECURITY</dc:creator>
      <pubDate>Mon, 04 May 2026 12:07:27 +0000</pubDate>
      <link>https://dev.to/d_security/how-i-coordinate-scripts-across-devices-without-open-ports-firebase-or-a-vps-1ipi</link>
      <guid>https://dev.to/d_security/how-i-coordinate-scripts-across-devices-without-open-ports-firebase-or-a-vps-1ipi</guid>
      <description>&lt;p&gt;My scrapers run on PythonAnywhere. My phone runs Termux. I wanted them to talk to each other.&lt;/p&gt;

&lt;p&gt;The standard options all had the same problem: they required infrastructure I didn't want to maintain.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Firebase&lt;/strong&gt; — cloud lock-in, SDK overhead, costs money at scale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ngrok&lt;/strong&gt; — exposes a port on my phone, dies when the tunnel resets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A VPS with Redis&lt;/strong&gt; — another server to maintain, SSH into, keep alive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook to phone&lt;/strong&gt; — my phone is behind NAT, no open ports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fundamental issue: cloud scripts push, but phones can't receive pushes without an exposed port or a cloud intermediary.&lt;/p&gt;

&lt;p&gt;So I flipped the model. Instead of the cloud pushing to the phone, the phone &lt;em&gt;pulls&lt;/em&gt; from a neutral server. Workers poll outbound. No open ports needed. Works behind any firewall.&lt;/p&gt;

&lt;p&gt;That's &lt;a href="https://github.com/dsecurity49/Intent-Bus" rel="noopener noreferrer"&gt;Intent Bus&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;A lightweight Flask server sits between your scripts and your workers. Scripts post jobs ("intents") to it. Workers anywhere poll for jobs matching their goal, claim them atomically, execute them, and report back.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────┐
│  Cloud Script                     │ (PythonAnywhere, VPS, Lambda)
│  POST /intent                     │
└──────────┬──────────┘
                  │
                  ▼
┌─────────────────────┐
│  Intent Bus                       │  (Flask + SQLite — free tier works)
│  /intent                          │
│  /claim                           │
│  /fulfill                         │
└──────────┬──────────┘
                  │
        ┌─────┴──────┐
        ▼                    ▼
┌─────────┐  ┌─────────┐
│ Termux        │  │ Linux         │
│ Worker        │  │ Worker        │
│ (phone)       │  │ (Pi, PC).     │
└─────────┘  └─────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The phone worker polls every 10 seconds. When there's a job, it claims it, runs it, and marks it fulfilled. If the worker crashes mid-job, the 60-second visibility timeout auto-requeues it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setup in 3 Steps
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Deploy the server (free)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Fork the repo and deploy &lt;code&gt;flask_app.py&lt;/code&gt; to PythonAnywhere. Set one environment variable:&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;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BUS_SECRET&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;your_secret_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The server is live.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Start a worker on your phone&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Termux:&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"your_api_key"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.apikey
pkg &lt;span class="nb"&gt;install &lt;/span&gt;jq curl termux-api
bash worker.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The worker boots up and starts polling for &lt;code&gt;send_notification&lt;/code&gt; intents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 — Trigger it from anywhere&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://your-bus.pythonanywhere.com/intent &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"goal":"send_notification","payload":{"message":"Scrape complete"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your phone buzzes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real Use Cases
&lt;/h2&gt;

&lt;p&gt;Because workers are just scripts, any device that can run bash or Python can be a worker:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phone notifications from cloud scripts&lt;/strong&gt;&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;# worker.sh on Termux&lt;/span&gt;
termux-notification &lt;span class="nt"&gt;--title&lt;/span&gt; &lt;span class="s2"&gt;"Alert"&lt;/span&gt; &lt;span class="nt"&gt;--content&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MESSAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Log events from multiple machines to one file&lt;/strong&gt;&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;# logger.sh on any Linux box&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="nv"&gt;$TEXT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; bus.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Relay alerts to Discord&lt;/strong&gt;&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;# discord_worker.sh&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DISCORD_WEBHOOK&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;content&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$MESSAGE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Trigger a deploy on a home Raspberry Pi behind a firewall&lt;/strong&gt;&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="c1"&gt;# python_worker.py on Pi
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;goal&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deploy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git&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;pull&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;systemctl&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;restart&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;myapp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Pi never needs an open port. It polls out, picks up the deploy job, and runs it.&lt;/p&gt;




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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;intent-bus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;intent_bus&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;IntentClient&lt;/span&gt;

&lt;span class="n"&gt;bus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;IntentClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# reads key from ~/.apikey automatically
&lt;/span&gt;
&lt;span class="c1"&gt;# From your cloud script
&lt;/span&gt;&lt;span class="n"&gt;bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;send_notification&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&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;message&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;Done&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# On your phone/Pi/worker machine
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;system&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;termux-notification --content &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&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="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;send_notification&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What This Doesn't Do
&lt;/h2&gt;

&lt;p&gt;Be honest about limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Not for high-frequency workloads&lt;/strong&gt; — SQLite serializes writes, ceiling around 50-100/sec&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not exactly-once delivery&lt;/strong&gt; — at-least-once, workers should be idempotent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not multi-region&lt;/strong&gt; — single SQLite node, no replication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not a Firebase replacement&lt;/strong&gt; — no real-time push, no client SDKs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's designed for the gap between cron scripts and full distributed systems. Scripts, bots, scrapers, phone notifications, home automation — workloads where Redis is overkill and cron is too dumb.&lt;/p&gt;




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

&lt;p&gt;Server + workers: &lt;strong&gt;&lt;a href="https://github.com/dsecurity49/Intent-Bus" rel="noopener noreferrer"&gt;https://github.com/dsecurity49/Intent-Bus&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Python SDK: &lt;strong&gt;&lt;a href="https://github.com/dsecurity49/intent-bus-sdk" rel="noopener noreferrer"&gt;https://github.com/dsecurity49/intent-bus-sdk&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;intent-bus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Live demo instance available — open a GitHub Discussion or join the Discord to request a free tester key.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What cross-device automation have you built with Termux? I'm curious what use cases I haven't thought of yet.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>termux</category>
      <category>flask</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why I Built a Job Queue With SQLite Instead of Redis (And What I Learned)</title>
      <dc:creator>D_SECURITY</dc:creator>
      <pubDate>Sun, 03 May 2026 16:10:20 +0000</pubDate>
      <link>https://dev.to/d_security/why-i-built-a-job-queue-with-sqlite-instead-of-redis-and-what-i-learned-4f05</link>
      <guid>https://dev.to/d_security/why-i-built-a-job-queue-with-sqlite-instead-of-redis-and-what-i-learned-4f05</guid>
      <description>&lt;p&gt;I needed to coordinate background scripts running across different machines.&lt;/p&gt;

&lt;p&gt;The obvious answer was Redis. Everyone uses Redis for this. The tutorials all use Redis. The Stack Overflow answers all say "just use Redis."&lt;/p&gt;

&lt;p&gt;So I looked at what deploying Redis would actually cost me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A running Redis server I had to maintain&lt;/li&gt;
&lt;li&gt;A broker to connect workers to it&lt;/li&gt;
&lt;li&gt;Celery or RQ on top of that&lt;/li&gt;
&lt;li&gt;Memory-based storage with no persistence by default&lt;/li&gt;
&lt;li&gt;A whole new failure mode: what happens when Redis goes down?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm running scripts on PythonAnywhere and a phone running Termux. I don't want to maintain infrastructure. I just want my cloud script to say "hey, do this" and have a worker somewhere execute it.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/dsecurity49/Intent-Bus" rel="noopener noreferrer"&gt;Intent Bus&lt;/a&gt; — a pure HTTP job queue backed by SQLite. No Redis. No broker. No open ports required.&lt;/p&gt;

&lt;p&gt;Here's what I learned.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;p&gt;Any script POSTs a job to a Flask server. Workers anywhere poll for jobs matching their goal, claim them atomically, execute them, and mark them fulfilled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cloud Script
    │
    │ POST /intent  {"goal": "send_notification", "payload": {...}}
    ▼
Intent Bus (Flask + SQLite)
    │
    │ POST /claim?goal=send_notification
    ▼
Worker (Termux / Linux / anywhere)
    │
    │ executes job → POST /fulfill/&amp;lt;id&amp;gt;
    ▼
Done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Workers poll &lt;em&gt;outbound&lt;/em&gt;. They don't need an open port. They work behind any firewall.&lt;/p&gt;




&lt;h2&gt;
  
  
  The SQLite Locking Question
&lt;/h2&gt;

&lt;p&gt;The part everyone pushes back on: &lt;em&gt;can SQLite actually handle concurrent workers without race conditions?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Yes — with the right setup. Here's the exact claim query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="k"&gt;IMMEDIATE&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;candidate&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;intents&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;expires_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'failed'&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;claim_attempts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;max_attempts&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'open'&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'claimed'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;claimed_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;publisher&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;worker_key&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'public'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;target_goal&lt;/span&gt;
    &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;claim_attempts&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
    &lt;span class="k"&gt;LIMIT&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;UPDATE&lt;/span&gt; &lt;span class="n"&gt;intents&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'claimed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;claimed_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;claimed_by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;claimer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;claim_attempts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;claim_attempts&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;claim_attempts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;max_attempts&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'open'&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'claimed'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;claimed_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;stale&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;RETURNING&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;claim_attempts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;BEGIN IMMEDIATE&lt;/code&gt; acquires a write lock before the CTE evaluates. The entire block — candidate selection, &lt;code&gt;UPDATE&lt;/code&gt;, and &lt;code&gt;RETURNING&lt;/code&gt; — is a single atomic statement. No other writer can start a transaction until this one commits. One worker gets the job; every other concurrent claim hits the &lt;code&gt;claim_attempts &amp;lt; :max_attempts&lt;/code&gt; guard or finds no candidate and gets a &lt;code&gt;204&lt;/code&gt; back.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;PRAGMA journal_mode=WAL&lt;/code&gt; and &lt;code&gt;PRAGMA busy_timeout=30000&lt;/code&gt;, readers never block writers and the system handles short bursts cleanly.&lt;/p&gt;

&lt;p&gt;In practice on PythonAnywhere's free tier: tested with 3 concurrent workers, zero double-claims.&lt;/p&gt;




&lt;h2&gt;
  
  
  What SQLite Gets You That Redis Doesn't
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;SQLite (Intent Bus)&lt;/th&gt;
&lt;th&gt;Redis + Celery&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure&lt;/td&gt;
&lt;td&gt;Zero — just a file&lt;/td&gt;
&lt;td&gt;Redis server + broker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistence&lt;/td&gt;
&lt;td&gt;ACID by default&lt;/td&gt;
&lt;td&gt;Requires AOF/RDB config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inspect queue&lt;/td&gt;
&lt;td&gt;Any SQL tool&lt;/td&gt;
&lt;td&gt;Redis CLI or dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-hostable&lt;/td&gt;
&lt;td&gt;Yes, free tier works&lt;/td&gt;
&lt;td&gt;Needs paid hosting or VPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile workers&lt;/td&gt;
&lt;td&gt;Yes, just HTTP&lt;/td&gt;
&lt;td&gt;Complex from Termux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure recovery&lt;/td&gt;
&lt;td&gt;60s visibility timeout, auto-requeue&lt;/td&gt;
&lt;td&gt;Ack-based, more complex&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Throughput ceiling&lt;/td&gt;
&lt;td&gt;~50-100 writes/sec&lt;/td&gt;
&lt;td&gt;Tens of thousands/sec&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The throughput ceiling is the honest limitation. SQLite WAL mode serializes writes. At high concurrency (&amp;gt;10 req/sec sustained) you'll see queue buildup. For that use case, use Redis — it's the right tool.&lt;/p&gt;

&lt;p&gt;For low-to-medium frequency workloads (scripts, bots, scrapers, phone notifications), SQLite is not just adequate — it's &lt;em&gt;better&lt;/em&gt;, because you get persistence, inspectability, and zero infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Delivery Guarantees
&lt;/h2&gt;

&lt;p&gt;Intent Bus provides &lt;strong&gt;at-least-once delivery&lt;/strong&gt;. If a worker crashes after claiming but before fulfilling, the 60-second visibility timeout auto-requeues the job. The next available worker picks it up.&lt;/p&gt;

&lt;p&gt;This means workers should be idempotent where possible. For most script automation use cases (send a notification, write a log, trigger a webhook), this is fine.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Scale Ceiling
&lt;/h2&gt;

&lt;p&gt;I get asked: "what happens when this gets popular?"&lt;/p&gt;

&lt;p&gt;The upgrade path is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Swap SQLite for PostgreSQL — minimal code changes, the query logic is identical&lt;/li&gt;
&lt;li&gt;Move to a paid VPS for higher CPU allocation&lt;/li&gt;
&lt;li&gt;Split read/write paths if needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But the honest answer is: if you need to worry about that upgrade path, Intent Bus isn't the right tool for you &lt;em&gt;today&lt;/em&gt;. It's designed for the tier below that problem.&lt;/p&gt;




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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;intent-bus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;intent_bus&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;IntentClient&lt;/span&gt;

&lt;span class="n"&gt;bus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;IntentClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_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;your_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Publish a job
&lt;/span&gt;&lt;span class="n"&gt;bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;send_notification&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&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;message&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;Scrape complete&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# Listen for jobs
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&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="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;bus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;send_notification&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full server code, spec, and worker examples: &lt;strong&gt;&lt;a href="https://github.com/dsecurity49/Intent-Bus" rel="noopener noreferrer"&gt;https://github.com/dsecurity49/Intent-Bus&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The server runs free on PythonAnywhere. Workers run anywhere that can make HTTP requests — including a phone.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you used SQLite as a message broker in production? I'd genuinely like to know where it held up and where it broke down — drop a comment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>sqlite</category>
      <category>flask</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How I Control My Android Phone From a Cloud Server Using 100 Lines of Flask</title>
      <dc:creator>D_SECURITY</dc:creator>
      <pubDate>Fri, 24 Apr 2026 08:29:05 +0000</pubDate>
      <link>https://dev.to/d_security/how-i-control-my-android-phone-from-a-cloud-server-using-100-lines-of-flask-2fl6</link>
      <guid>https://dev.to/d_security/how-i-control-my-android-phone-from-a-cloud-server-using-100-lines-of-flask-2fl6</guid>
      <description>&lt;p&gt;My background scrapers run on PythonAnywhere. My phone runs Termux. I wanted the scrapers to ping my phone when something interesting happened — without Firebase, without Ngrok, without paying for anything.&lt;/p&gt;

&lt;p&gt;So I built Intent Bus.&lt;/p&gt;

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

&lt;p&gt;Most cross-device automation forces you to choose between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Too simple:&lt;/strong&gt; hardcoded cron jobs with no coordination&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Too complex:&lt;/strong&gt; Redis, RabbitMQ, Firebase — full infrastructure for a simple notification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I just wanted my cloud script to say "hey, buzz my phone." That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;Intent Bus is a tiny Flask app backed by SQLite. It has one job: let scripts post work, and let workers claim and execute that work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Cloud script posts an intent:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://your-bus.pythonanywhere.com/intent &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: your_key_here"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"goal":"send_notification","payload":{"message":"Scrape complete"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2 — Termux worker claims it:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A bash script running on my phone polls the bus every 10 seconds. When it sees a job, it claims it with an atomic lock, executes it, and marks it fulfilled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 — Phone buzzes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;termux-notification &lt;span class="nt"&gt;--title&lt;/span&gt; &lt;span class="s2"&gt;"System Update"&lt;/span&gt; &lt;span class="nt"&gt;--content&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MESSAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the entire flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes It Reliable
&lt;/h2&gt;

&lt;p&gt;Three things I had to get right:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Atomic locking&lt;/strong&gt; — SQLite's UPDATE with rowcount check ensures only one worker ever claims a job, even with multiple workers running simultaneously.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visibility timeout&lt;/strong&gt; — if a worker crashes mid-job, the lock expires after 60 seconds and the job goes back into the queue automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Topic routing&lt;/strong&gt; — workers only claim jobs matching their goal via &lt;code&gt;?goal=&lt;/code&gt;. A notification worker never accidentally picks up a logging job.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cloud Scraper (PythonAnywhere)
        |
        | POST /intent
        ↓
Intent Bus (Flask + SQLite)
        |
        | claim + fulfill
        ↓
Termux Worker (Android Phone)
        |
        | termux-notification
        ↓
📱 Phone Notification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What It Can Do
&lt;/h2&gt;

&lt;p&gt;Because the bus is just HTTP, any script anywhere can post or claim jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scraper finishes → notify phone&lt;/li&gt;
&lt;li&gt;Website goes down → alert Discord&lt;/li&gt;
&lt;li&gt;GitHub push → trigger deploy on home Raspberry Pi behind a firewall&lt;/li&gt;
&lt;li&gt;Old Android phone → free Twilio replacement via &lt;code&gt;termux-sms-send&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;After posting this publicly, people immediately stress-tested the open endpoint. Fair. I added API key auth — key stored in PythonAnywhere's WSGI environment, never in the repo. Workers read from a local &lt;code&gt;.apikey&lt;/code&gt; file.&lt;/p&gt;

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

&lt;p&gt;The full code, spec, and examples are on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/dsecurity49/Intent-Bus" rel="noopener noreferrer"&gt;https://github.com/dsecurity49/Intent-Bus&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It runs free on PythonAnywhere + any Android phone with Termux. No Docker, no dependencies beyond Flask.&lt;/p&gt;

&lt;p&gt;If you build a worker script for it, PRs are welcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update: v7 is Live
&lt;/h2&gt;

&lt;p&gt;Since the original post, Intent Bus has been hardened significantly based on real-world feedback:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rate limiting (60 req/min per key+IP)&lt;/li&gt;
&lt;li&gt;Tester key system — request a free key to try the live instance&lt;/li&gt;
&lt;li&gt;Intent expiry after 24 hours&lt;/li&gt;
&lt;li&gt;Python worker for non-bash users&lt;/li&gt;
&lt;li&gt;Security headers and payload size limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DM me on Dev.to, open a GitHub Issue, or join the Discord to request a free tester key: &lt;a href="https://discord.gg/bzAneAQzGX" rel="noopener noreferrer"&gt;https://discord.gg/bzAneAQzGX&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>flask</category>
      <category>termux</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
