<?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>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>Update: The Python SDK is officially live on PyPI.
 pip install intent-bus
 GitHub: https://github.com/dsecurity49/intent-bus-sdk</title>
      <dc:creator>D_SECURITY</dc:creator>
      <pubDate>Thu, 30 Apr 2026 10:46:40 +0000</pubDate>
      <link>https://dev.to/d_security/update-the-python-sdk-is-officially-live-on-pypi-pip-install-intent-bus-github-1ni4</link>
      <guid>https://dev.to/d_security/update-the-python-sdk-is-officially-live-on-pypi-pip-install-intent-bus-github-1ni4</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/d_security/how-i-control-my-android-phone-from-a-cloud-server-using-100-lines-of-flask-2fl6" class="crayons-story__hidden-navigation-link"&gt;How I Control My Android Phone From a Cloud Server Using 100 Lines of Flask&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/d_security" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3895597%2F93c8a854-5160-40ca-bab5-d8a8f2d5b7bb.png" alt="d_security profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/d_security" class="crayons-story__secondary fw-medium m:hidden"&gt;
              D_SECURITY
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                D_SECURITY
                
              
              &lt;div id="story-author-preview-content-3545052" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/d_security" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3895597%2F93c8a854-5160-40ca-bab5-d8a8f2d5b7bb.png" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;D_SECURITY&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/d_security/how-i-control-my-android-phone-from-a-cloud-server-using-100-lines-of-flask-2fl6" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 24&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/d_security/how-i-control-my-android-phone-from-a-cloud-server-using-100-lines-of-flask-2fl6" id="article-link-3545052"&gt;
          How I Control My Android Phone From a Cloud Server Using 100 Lines of Flask
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/python"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;python&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/flask"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;flask&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/termux"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;termux&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/automation"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;automation&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/d_security/how-i-control-my-android-phone-from-a-cloud-server-using-100-lines-of-flask-2fl6" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt; reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/d_security/how-i-control-my-android-phone-from-a-cloud-server-using-100-lines-of-flask-2fl6#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              1&lt;span class="hidden s:inline"&gt; comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://github.com/dsecurity49/intent-bus-sdk" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopengraph.githubassets.com%2F257bf88fd84f00da025aab6d8c7f5e666798cb1fdf8334bbf0790c2d3f7f773a%2Fdsecurity49%2FIntent-Bus-sdk" height="600" class="m-0" width="1200"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://github.com/dsecurity49/intent-bus-sdk" rel="noopener noreferrer" class="c-link"&gt;
            GitHub - dsecurity49/Intent-Bus-sdk · GitHub
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Contribute to dsecurity49/Intent-Bus-sdk development by creating an account on GitHub.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.githubassets.com%2Ffavicons%2Ffavicon.svg" width="32" height="32"&gt;
          github.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>android</category>
      <category>opensource</category>
      <category>python</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>
