<?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 Stressed My SQLite Job Queue to 5,000 Continuous Tasks on an Android Phone (And Why It Outperformed the Cloud)</title>
      <dc:creator>D_SECURITY</dc:creator>
      <pubDate>Tue, 26 May 2026 15:30:06 +0000</pubDate>
      <link>https://dev.to/d_security/how-i-stressed-my-sqlite-job-queue-to-5000-continuous-tasks-on-an-android-phone-and-why-it-2kpc</link>
      <guid>https://dev.to/d_security/how-i-stressed-my-sqlite-job-queue-to-5000-continuous-tasks-on-an-android-phone-and-why-it-2kpc</guid>
      <description>&lt;p&gt;Every developer building a side project or a home automation pipeline eventually hits the same roadblock. You have a script running in the cloud (maybe a web scraper, a webhook handler, or an AI agent), and you need it to trigger a physical action on a local device—like an old Android phone running Termux, or a Raspberry Pi behind a strict home firewall.&lt;/p&gt;

&lt;p&gt;The standard industry advice is immediate: &lt;em&gt;"Just spin up Celery and back it with RabbitMQ or Redis."&lt;/em&gt; But for independent developers, indie hackers, and hobbyists, that answer feels terrible. Deploying and maintaining a heavy, memory-hungry message broker infrastructure for low-to-medium workloads is massive operational overkill. It’s expensive,&lt;br&gt;
introduces vendor lock-in if you go the managed cloud route, and forces you to manage background daemons that eat up system resources. &lt;/p&gt;

&lt;p&gt;The alternative? Opening inbound network ports or setting up reverse SSH tunnels, which is a security nightmare.&lt;/p&gt;

&lt;p&gt;That is why I built &lt;strong&gt;Intent Bus&lt;/strong&gt;—a zero-infrastructure job coordination system powered entirely by a minimal Flask core and a local SQLite database. Instead of maintaining persistent stateful connections or heavy external brokers, your cloud script simply writes tasks to an HTTP endpoint, and your cross-device edge workers safely poll for jobs using basic outbound HTTP requests. It gives you atomic locking, priority scheduling, exponential backoffs, and dead-letter queues out of the box with zero operational friction.&lt;/p&gt;

&lt;p&gt;But when you tell developers you built a concurrent message queue on top of SQLite, the skepticism is instant. &lt;em&gt;"SQLite has a single-writer bottleneck." "It will thrash the disk." "It can't scale under real worker contention."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We just launched live on Product Hunt, and my co-maintainer Zan and I decided to stop talking about theory. We decided to break it. Here is how we stress-tested Intent Bus across the cloud, the limits we hit, and the completely unexpected way an old smartphone stole the show.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Testing Strategy: Finding the Ceiling
&lt;/h2&gt;

&lt;p&gt;To find exactly where our architectural trade-offs would give out, we established a rigorous profiling matrix across three distinct deployment environments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;PythonAnywhere (Free Tier):&lt;/strong&gt; A standard python hosting environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Render (Free Tier Docker Container):&lt;/strong&gt; A typical lightweight cloud container setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Android 12 Device (Termux via local ARM CPU &amp;amp; Flash Storage):&lt;/strong&gt; True edge hosting.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We subjected the broker to progressively brutal worker/job ratios to watch the latency curve and catch database locks or lease drops in real-time.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Profile&lt;/th&gt;
&lt;th&gt;Concurrency Workload&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Low&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5 active workers polling and fulfilling 50 total jobs.&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 active workers polling and fulfilling 500 total jobs.&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 active workers polling and fulfilling 2,000 total jobs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Extreme&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100+ active workers polling and fulfilling 5,000 continuous jobs.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Phase 1: The Cloud Baseline (PythonAnywhere vs. Render)
&lt;/h2&gt;

&lt;p&gt;We kicked off our benchmarks on a &lt;strong&gt;PythonAnywhere Free Tier&lt;/strong&gt; instance. The results were an immediate failure under any real load. Because the free tier utilizes single-threaded request handling, concurrent workers polling the queue quickly created an unrecoverable request backlog. At medium load, it hit 100% network errors. Single-threaded processes are completely non-viable for worker polling loops.&lt;/p&gt;

&lt;p&gt;Next, we shifted to a multi-threaded &lt;strong&gt;Docker deployment on Render's free tier&lt;/strong&gt;. This is where the SQLite architecture started to shine. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On &lt;strong&gt;Medium load&lt;/strong&gt;, Render sailed through with a &lt;strong&gt;99.5% success rate&lt;/strong&gt;, averaging &lt;strong&gt;11.30 jobs/sec&lt;/strong&gt; with a tight P99 worker latency of just 0.517 seconds. &lt;/li&gt;
&lt;li&gt;When we pushed it to &lt;strong&gt;Heavy load&lt;/strong&gt; (40 workers, 2,000 jobs), the system held steady at a &lt;strong&gt;99.34% success rate&lt;/strong&gt; and maintained a &lt;strong&gt;14.18 jobs/sec&lt;/strong&gt; throughput. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because we engineered Intent Bus around a strictly optimized SQLite Write-Ahead Logging (WAL) configuration, the database handled concurrent reads comfortably. Under heavy writer contention, our locking mechanism gracefully degraded—tail latencies extended out to a P99 of 2.891 seconds, but the queue never crashed, never locked up, and leaked exactly zero jobs.&lt;/p&gt;

&lt;h2&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%2Farticles%2F0ky9zjx3dx9eb6zq2ave.jpg" alt="Render stress test" width="799" height="528"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Phase 2: The Android Experiment (Pushing to the Extreme)
&lt;/h2&gt;

&lt;p&gt;With a stable cloud baseline of ~14 jobs/sec, we decided to run an unconventional experiment: What happens if we host the central message broker right on the edge? We spun up the Intent Bus server inside &lt;strong&gt;Termux on a standard Android 12 phone&lt;/strong&gt;, using its internal ARM processor and mobile flash storage as the entire infrastructure backbone.&lt;/p&gt;

&lt;p&gt;The results completely blew past our expectations.&lt;/p&gt;

&lt;p&gt;When we unleashed the &lt;strong&gt;Heavy load test&lt;/strong&gt; (40 workers, 2,000 jobs) against the smartphone server, the mobile hardware didn't just keep up—it completely crushed the cloud container, clocking an astonishing &lt;strong&gt;28.04 jobs per second&lt;/strong&gt; with a P99 latency of 2.556 seconds. Because the physical phone flash storage didn't have to contend with the shared cloud hypervisor overhead found on free-tier containers, SQLite local disk I/O operations executed at blistering speeds.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2F9ljqto4gpsy0p3uji3en.jpg" class="article-body-image-wrapper"&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%2Farticles%2F9ljqto4gpsy0p3uji3en.jpg" alt="Android stress test" width="799" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pushing to the Absolute Brink: 5,000 Continuous Jobs
&lt;/h3&gt;

&lt;p&gt;Emboldened by the phone's performance, we threw our absolute worst-case scenario at it: &lt;strong&gt;The Extreme Profile&lt;/strong&gt;. We unleashed over 100 concurrent worker loops, firing a non-stop barrage of &lt;strong&gt;5,000 jobs over a sustained 4.5-minute beating (267 seconds)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is what real system engineering looks like when it hits a hardware wall:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Graceful Degradation:&lt;/strong&gt; As the mobile flash storage and ARM chip finally throttled under the sustained heat and massive file-write contention, our average throughput dropped from its peak down to a sustained &lt;strong&gt;18.52 jobs/sec&lt;/strong&gt;. Tail latencies spiked heavily, pushing the P99 worker response out to 9.002 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Absolute Structural Integrity:&lt;/strong&gt; Despite the hardware bottleneck, look at the error counters. &lt;strong&gt;Zero network drops. Zero lease losses. Zero publish rejects.&lt;/strong&gt; Even when the device was gasping for air, the protocol's locking logic and database state transitions remained flawless. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Final Score:&lt;/strong&gt; The system completed the marathon with a &lt;strong&gt;98.89% success rate&lt;/strong&gt; over 5,000 continuous tasks, proving it is virtually indestructible under load.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fheayk7xm01ft1gnz1r43.jpg" class="article-body-image-wrapper"&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%2Farticles%2Fheayk7xm01ft1gnz1r43.jpg" alt=" " width="800" height="664"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Takeaways: Why This Matters for Indie Devs
&lt;/h2&gt;

&lt;p&gt;We didn't build Intent Bus to replace Kafka, RabbitMQ, or high-throughput enterprise systems. If you are building a multi-region corporate platform processing tens of thousands of requests per second, you need a traditional distributed broker architecture. &lt;/p&gt;

&lt;p&gt;But if you are an indie hacker, a DevOps engineer tying personal scripts together, or an automation enthusiast coordinating a heterogeneous fleet of edge machines, these benchmarks change things. &lt;/p&gt;

&lt;p&gt;They prove that &lt;strong&gt;a lightweight, properly optimized SQLite WAL backend is fundamentally robust enough&lt;/strong&gt; to handle thousands of concurrent jobs with zero data corruption. It means you can completely bypass the cost and complexity of cloud server infrastructure. You can literally pull an old phone out of a drawer, install Termux, and host an incredibly reliable, self-contained, bulletproof automation queue entirely for free.&lt;/p&gt;




&lt;h2&gt;
  
  
  We are Live on Product Hunt!
&lt;/h2&gt;

&lt;p&gt;Intent Bus is 100% open-source and written in Python. While we provide a dedicated Python SDK to handle signature generation and worker loops automatically, the underlying protocol is lightweight enough to interact with using nothing but pure &lt;code&gt;bash&lt;/code&gt; and &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Our code is fully verified, our CI/CD pipelines are green, and our Product Hunt launch is officially live right now! If you want to check out the codebase, audit our security protocol updates, or leave us your honest feedback, check out the links below:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Leave a Review on Product Hunt:&lt;/strong&gt; &lt;a href="https://www.producthunt.com/products/intent-bus/reviews/new" rel="noopener noreferrer"&gt;https://www.producthunt.com/products/intent-bus/reviews/new&lt;/a&gt;&lt;br&gt;
👉 &lt;strong&gt;GitHub Main Server:&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;br&gt;
👉 &lt;strong&gt;GitHub SDK Repo:&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;/p&gt;

&lt;p&gt;&lt;em&gt;I’ll be hanging out in the Product Hunt comments section all day to answer your questions—I'd love to hear your thoughts on our SQLite implementation, our capability routing, or our edge-computing performance results!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>devops</category>
      <category>backend</category>
    </item>
    <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>
