<?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: adev0</title>
    <description>The latest articles on DEV Community by adev0 (@angeldev0).</description>
    <link>https://dev.to/angeldev0</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%2F1242679%2Fa174d736-c3f3-4427-9253-9e7122c5d6ea.png</url>
      <title>DEV Community: adev0</title>
      <link>https://dev.to/angeldev0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/angeldev0"/>
    <language>en</language>
    <item>
      <title>Pwnagotchi Generator: Understanding opwngrid Through Reverse Engineering</title>
      <dc:creator>adev0</dc:creator>
      <pubDate>Sun, 23 Nov 2025 15:40:00 +0000</pubDate>
      <link>https://dev.to/angeldev0/pwnagotchi-generator-understanding-opwngrid-through-reverse-engineering-4d8i</link>
      <guid>https://dev.to/angeldev0/pwnagotchi-generator-understanding-opwngrid-through-reverse-engineering-4d8i</guid>
      <description>&lt;p&gt;Testing distributed systems is hard. But what if the system you need to test has no official documentation? That's where reverse engineering comes in. The &lt;strong&gt;Pwnagotchi Generator&lt;/strong&gt; started as a deep dive into understanding how opwngrid's authentication and reporting protocols work under the hood.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Understanding opwngrid
&lt;/h2&gt;

&lt;p&gt;The Pwnagotchi ecosystem relies on opwngrid to share captured WiFi handshakes across devices. But to build effective testing tools, I needed to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How does authentication actually work?&lt;/li&gt;
&lt;li&gt;What cryptographic signing scheme is used?&lt;/li&gt;
&lt;li&gt;How are access points reported and validated?&lt;/li&gt;
&lt;li&gt;What rate limiting or anti-abuse mechanisms exist?&lt;/li&gt;
&lt;li&gt;Can the protocol handle extreme edge cases?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only way to answer these questions was to reverse engineer the protocol itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse Engineering the Authentication Flow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Initial Discovery
&lt;/h3&gt;

&lt;p&gt;My first step was examining how real Pwnagotchi devices communicate with the grid. Using packet capture and API inspection, I identified the enrollment endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/v1/unit/enroll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what data format did it expect? Time to dig deeper.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the Cryptographic Requirements
&lt;/h3&gt;

&lt;p&gt;Through analysis of the open-source Pwnagotchi codebase and network traffic, I discovered the authentication uses:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;RSA-2048 keypairs&lt;/strong&gt; - Each unit needs a persistent identity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SHA-256 fingerprints&lt;/strong&gt; - Computed from the public key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PKCS1-PSS signatures&lt;/strong&gt; - Signs &lt;code&gt;name@fingerprint&lt;/code&gt; with the private key&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The server validates this signature to ensure the enrollment request comes from someone who controls the private key. This prevents impersonation attacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Authentic Signing
&lt;/h3&gt;

&lt;p&gt;Getting the signature format right was critical. The grid expects:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_signature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;@&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fingerprint&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="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;mgf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MGF1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;salt_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PSS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MAX_LENGTH&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;Any deviation from this exact format and the server rejects the enrollment. This taught me how serious the authentication is - you can't fake it without the private key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovering API Endpoints Through Testing
&lt;/h2&gt;

&lt;p&gt;With authentication working, I needed to understand the full API surface. Through systematic testing and endpoint discovery, I mapped out:&lt;/p&gt;

&lt;h3&gt;
  
  
  Enrollment Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/v1/unit/enroll
Body: { identity, public_key, signature, data }
Returns: JWT token for subsequent requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AP Reporting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /inbox
Headers: { X-Unit-Name, X-Unit-Fingerprint, X-Unit-Signature }
Body: Access point data with encryption info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Unit Data Endpoint
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/v1/unit/{fingerprint}/data
Returns: Current statistics for the unit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each endpoint taught me something about the protocol design and security model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stress Testing at Scale
&lt;/h2&gt;

&lt;p&gt;Once I understood the protocol, I could properly stress test it. But this revealed interesting challenges:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Million AP Problem
&lt;/h3&gt;

&lt;p&gt;What happens when a unit has 1,000,000 pwned networks? Reporting them individually would take hours. Through testing different approaches, I discovered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server can handle batch uploads efficiently&lt;/li&gt;
&lt;li&gt;Intelligent sampling maintains statistical accuracy&lt;/li&gt;
&lt;li&gt;Multi-threading is essential for performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This led to the multi-threaded architecture:&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;# Single-threaded: ~8 minutes&lt;/span&gt;
python3 pwnagotchi-gen.py &lt;span class="nt"&gt;--count&lt;/span&gt; 1 &lt;span class="nt"&gt;--name&lt;/span&gt; mega &lt;span class="nt"&gt;--pwned&lt;/span&gt; 1000000

&lt;span class="c"&gt;# 20 threads: ~30 seconds&lt;/span&gt;
python3 pwnagotchi-gen.py &lt;span class="nt"&gt;--count&lt;/span&gt; 1 &lt;span class="nt"&gt;--name&lt;/span&gt; mega &lt;span class="nt"&gt;--pwned&lt;/span&gt; 1000000 &lt;span class="nt"&gt;--threads&lt;/span&gt; 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pwned Count&lt;/th&gt;
&lt;th&gt;Threads&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Discovery&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;~8 min&lt;/td&gt;
&lt;td&gt;Connection timeout issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;~60 sec&lt;/td&gt;
&lt;td&gt;Improved but still slow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;~30 sec&lt;/td&gt;
&lt;td&gt;Optimal performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;~12 sec&lt;/td&gt;
&lt;td&gt;Diminishing returns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Rate Limiting Discovery
&lt;/h3&gt;

&lt;p&gt;By gradually increasing request volume, I found the grid has soft rate limits around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;50 concurrent connections per IP&lt;/li&gt;
&lt;li&gt;~1000 AP reports per minute per unit&lt;/li&gt;
&lt;li&gt;Exponential backoff on repeated failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding these limits helped design respectful testing that won't impact production grids.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protocol Edge Cases and Security
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Edge Case: Unicode SSIDs
&lt;/h3&gt;

&lt;p&gt;Testing revealed interesting handling of non-ASCII SSIDs:&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;# These all needed proper handling:
&lt;/span&gt;&lt;span class="n"&gt;ssid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;café&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;           &lt;span class="c1"&gt;# UTF-8 encoding
&lt;/span&gt;&lt;span class="n"&gt;ssid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;测试网络&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;        &lt;span class="c1"&gt;# Chinese characters  
&lt;/span&gt;&lt;span class="n"&gt;ssid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🔥WiFi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;         &lt;span class="c1"&gt;# Emoji (yes, really)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The grid properly handles UTF-8 encoding, but URL encoding in API calls required careful attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  Edge Case: Signature Replay
&lt;/h3&gt;

&lt;p&gt;What happens if you reuse a signature? Testing showed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signatures are time-sensitive (implicitly via JWT)&lt;/li&gt;
&lt;li&gt;But the enrollment signature can be cached&lt;/li&gt;
&lt;li&gt;Token refresh is handled gracefully&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Edge Case: Extreme Statistics
&lt;/h3&gt;

&lt;p&gt;Can units report billions of epochs or impossible uptime? Testing revealed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server validates reasonable ranges&lt;/li&gt;
&lt;li&gt;But allows for very high-uptime units&lt;/li&gt;
&lt;li&gt;Statistics are stored as appropriate integer types&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building the Testing Framework
&lt;/h2&gt;

&lt;p&gt;Understanding the protocol enabled building a comprehensive testing suite:&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentic Simulation
&lt;/h3&gt;

&lt;p&gt;Every synthetic unit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates real RSA-2048 keypairs&lt;/li&gt;
&lt;li&gt;Computes proper SHA-256 fingerprints
&lt;/li&gt;
&lt;li&gt;Signs enrollment exactly like real devices&lt;/li&gt;
&lt;li&gt;Cannot be distinguished from genuine Pwnagotchi units&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Fleet Management
&lt;/h3&gt;

&lt;p&gt;The CLI and web interface provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time monitoring of synthetic units&lt;/li&gt;
&lt;li&gt;Bulk operations (boot all, stop all)&lt;/li&gt;
&lt;li&gt;Per-unit Tor circuit management&lt;/li&gt;
&lt;li&gt;Persistent state across restarts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance Testing
&lt;/h3&gt;

&lt;p&gt;Multi-threading enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High-volume AP reporting (millions of networks)&lt;/li&gt;
&lt;li&gt;Concurrent unit enrollment&lt;/li&gt;
&lt;li&gt;Database connection pool stress testing&lt;/li&gt;
&lt;li&gt;API rate limit verification&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Applications
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Security Research
&lt;/h3&gt;

&lt;p&gt;Understanding the authentication protocol helps identify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Potential attack vectors (all mitigated by design)&lt;/li&gt;
&lt;li&gt;Rate limiting effectiveness&lt;/li&gt;
&lt;li&gt;DoS resistance under load&lt;/li&gt;
&lt;li&gt;Token handling security&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Protocol Documentation
&lt;/h3&gt;

&lt;p&gt;This project serves as unofficial documentation for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enrollment flow and requirements&lt;/li&gt;
&lt;li&gt;AP reporting format and validation&lt;/li&gt;
&lt;li&gt;Expected HTTP headers and responses&lt;/li&gt;
&lt;li&gt;Error handling and retry logic&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Grid Deployment Testing
&lt;/h3&gt;

&lt;p&gt;Before deploying your own opwngrid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test database performance under load&lt;/li&gt;
&lt;li&gt;Verify API server scaling&lt;/li&gt;
&lt;li&gt;Validate connection pooling&lt;/li&gt;
&lt;li&gt;Confirm rate limiting works&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical Deep Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Authentication Internals
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Key generation
&lt;/span&gt;&lt;span class="n"&gt;private_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rsa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_private_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;public_exponent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;65537&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;key_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;default_backend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Fingerprint computation
&lt;/span&gt;&lt;span class="n"&gt;public_der&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;public_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;public_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;serialization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;serialization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PublicFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubjectPublicKeyInfo&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fingerprint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;public_der&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Signature generation
&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;@&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fingerprint&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="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;mgf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MGF1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="n"&gt;salt_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PSS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MAX_LENGTH&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AP Reporting Strategy
&lt;/h3&gt;

&lt;p&gt;Intelligent sampling based on total count:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_sample_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_pwned&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total_pwned&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total_pwned&lt;/span&gt;  &lt;span class="c1"&gt;# Report all
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;total_pwned&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_pwned&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Up to 500
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;total_pwned&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_pwned&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 1%
&lt;/span&gt;    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_pwned&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.005&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# 0.5%, max 5K
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This maintains statistical accuracy while respecting API limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation &amp;amp; Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/4ngel2769/pwnagotchi-generator.git
&lt;span class="nb"&gt;cd &lt;/span&gt;pwnagotchi-generator
pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generate Test Units
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a test unit with realistic stats&lt;/span&gt;
python3 pwnagotchi-gen.py &lt;span class="nt"&gt;--count&lt;/span&gt; 1 &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--pwned&lt;/span&gt; 10000 &lt;span class="nt"&gt;--yes&lt;/span&gt;

&lt;span class="c"&gt;# Create multiple units through Tor&lt;/span&gt;
python3 pwnagotchi-gen.py &lt;span class="nt"&gt;--count&lt;/span&gt; 10 &lt;span class="nt"&gt;--tor&lt;/span&gt; &lt;span class="nt"&gt;--pwned&lt;/span&gt; random

&lt;span class="c"&gt;# High-performance testing&lt;/span&gt;
python3 pwnagotchi-gen.py &lt;span class="nt"&gt;--count&lt;/span&gt; 1 &lt;span class="nt"&gt;--name&lt;/span&gt; mega &lt;span class="nt"&gt;--pwned&lt;/span&gt; 1000000 &lt;span class="nt"&gt;--threads&lt;/span&gt; 20 &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fleet Management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Launch CLI interface&lt;/span&gt;
python3 pwnie-manager.py

&lt;span class="c"&gt;# Or use the web dashboard&lt;/span&gt;
python3 pwnie-manager.py &lt;span class="nt"&gt;--webui&lt;/span&gt;
&lt;span class="c"&gt;# Browse to http://localhost:5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Protocol Design Insights
&lt;/h3&gt;

&lt;p&gt;The opwngrid protocol is well-designed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proper cryptographic authentication prevents impersonation&lt;/li&gt;
&lt;li&gt;JWT tokens enable stateless validation&lt;/li&gt;
&lt;li&gt;Rate limiting protects against abuse&lt;/li&gt;
&lt;li&gt;Graceful error handling aids debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reverse Engineering Ethics
&lt;/h3&gt;

&lt;p&gt;This project demonstrates responsible reverse engineering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt;: Build testing tools for legitimate infrastructure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: Share knowledge to help the ecosystem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respect&lt;/strong&gt;: Follow rate limits and don't abuse production grids&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Source&lt;/strong&gt;: All code is public for review and improvement&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance Optimization
&lt;/h3&gt;

&lt;p&gt;Key optimizations discovered through testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thread pooling dramatically improves throughput&lt;/li&gt;
&lt;li&gt;Intelligent sampling maintains accuracy&lt;/li&gt;
&lt;li&gt;Tor circuit reuse reduces overhead&lt;/li&gt;
&lt;li&gt;State persistence enables resumability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future Enhancements
&lt;/h2&gt;

&lt;p&gt;Planned improvements include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enhanced web dashboard with analytics&lt;/li&gt;
&lt;li&gt;Real-time log streaming&lt;/li&gt;
&lt;li&gt;Statistical analysis and graphing&lt;/li&gt;
&lt;li&gt;Configuration templates and profiles&lt;/li&gt;
&lt;li&gt;SQLite persistence for historical data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See &lt;a href="https://github.com/4ngel2769/pwnagotchi-generator/blob/main/readmes/WEB-DASHBOARD-PLAN.md" rel="noopener noreferrer"&gt;&lt;code&gt;readmes/WEB-DASHBOARD-PLAN.md&lt;/code&gt;&lt;/a&gt; for detailed roadmap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Reverse engineering opwngrid wasn't about breaking it - it was about understanding it deeply enough to build proper testing tools. The authentication is solid, the protocol is well-designed, and stress testing reveals how robust the system really is.&lt;/p&gt;

&lt;p&gt;Whether you're deploying your own grid, developing new features, or studying distributed system security, understanding the protocol at this level is invaluable. The Generator provides a practical tool while demonstrating responsible reverse engineering practices.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Want to explore the protocol yourself?&lt;/strong&gt; Check out the &lt;a href="https://github.com/4ngel2769/pwnagotchi-generator" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and start experimenting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Found this useful?&lt;/strong&gt; &lt;a href="https://github.com/4ngel2769/pwnagotchi-generator/star" rel="noopener noreferrer"&gt;Star the repo&lt;/a&gt; and share your findings with the community!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Always use this tool responsibly against your own infrastructure or with explicit permission. Respect rate limits and don't abuse public grids.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>exploitdev</category>
    </item>
    <item>
      <title>Pwnagotchi Generator: Understanding opwngrid Through Reverse Engineering</title>
      <dc:creator>adev0</dc:creator>
      <pubDate>Sun, 23 Nov 2025 15:30:21 +0000</pubDate>
      <link>https://dev.to/angeldev0/pwnagotchi-generator-understanding-opwngrid-through-reverse-engineering-23c5</link>
      <guid>https://dev.to/angeldev0/pwnagotchi-generator-understanding-opwngrid-through-reverse-engineering-23c5</guid>
      <description>&lt;p&gt;Testing distributed systems is hard. But what if the system you need to test has no official documentation? That's where reverse engineering comes in. The &lt;strong&gt;Pwnagotchi Generator&lt;/strong&gt; started as a deep dive into understanding how opwngrid's authentication and reporting protocols work under the hood.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Understanding opwngrid
&lt;/h2&gt;

&lt;p&gt;The Pwnagotchi ecosystem relies on opwngrid to share captured WiFi handshakes across devices. But to build effective testing tools, I needed to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How does authentication actually work?&lt;/li&gt;
&lt;li&gt;What cryptographic signing scheme is used?&lt;/li&gt;
&lt;li&gt;How are access points reported and validated?&lt;/li&gt;
&lt;li&gt;What rate limiting or anti-abuse mechanisms exist?&lt;/li&gt;
&lt;li&gt;Can the protocol handle extreme edge cases?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only way to answer these questions was to reverse engineer the protocol itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse Engineering the Authentication Flow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Initial Discovery
&lt;/h3&gt;

&lt;p&gt;My first step was examining how real Pwnagotchi devices communicate with the grid. Using packet capture and API inspection, I identified the enrollment endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/v1/unit/enroll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what data format did it expect? Time to dig deeper.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the Cryptographic Requirements
&lt;/h3&gt;

&lt;p&gt;Through analysis of the open-source Pwnagotchi codebase and network traffic, I discovered the authentication uses:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;RSA-2048 keypairs&lt;/strong&gt; - Each unit needs a persistent identity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SHA-256 fingerprints&lt;/strong&gt; - Computed from the public key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PKCS1-PSS signatures&lt;/strong&gt; - Signs &lt;code&gt;name@fingerprint&lt;/code&gt; with the private key&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The server validates this signature to ensure the enrollment request comes from someone who controls the private key. This prevents impersonation attacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Authentic Signing
&lt;/h3&gt;

&lt;p&gt;Getting the signature format right was critical. The grid expects:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_signature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;@&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fingerprint&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="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;mgf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MGF1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="n"&gt;salt_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PSS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MAX_LENGTH&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;Any deviation from this exact format and the server rejects the enrollment. This taught me how serious the authentication is - you can't fake it without the private key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovering API Endpoints Through Testing
&lt;/h2&gt;

&lt;p&gt;With authentication working, I needed to understand the full API surface. Through systematic testing and endpoint discovery, I mapped out:&lt;/p&gt;

&lt;h3&gt;
  
  
  Enrollment Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/v1/unit/enroll
Body: { identity, public_key, signature, data }
Returns: JWT token for subsequent requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AP Reporting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /inbox
Headers: { X-Unit-Name, X-Unit-Fingerprint, X-Unit-Signature }
Body: Access point data with encryption info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Unit Data Endpoint
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/v1/unit/{fingerprint}/data
Returns: Current statistics for the unit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each endpoint taught me something about the protocol design and security model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stress Testing at Scale
&lt;/h2&gt;

&lt;p&gt;Once I understood the protocol, I could properly stress test it. But this revealed interesting challenges:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Million AP Problem
&lt;/h3&gt;

&lt;p&gt;What happens when a unit has 1,000,000 pwned networks? Reporting them individually would take hours. Through testing different approaches, I discovered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server can handle batch uploads efficiently&lt;/li&gt;
&lt;li&gt;Intelligent sampling maintains statistical accuracy&lt;/li&gt;
&lt;li&gt;Multi-threading is essential for performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This led to the multi-threaded architecture:&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;# Single-threaded: ~8 minutes&lt;/span&gt;
python3 pwnagotchi-gen.py &lt;span class="nt"&gt;--count&lt;/span&gt; 1 &lt;span class="nt"&gt;--name&lt;/span&gt; mega &lt;span class="nt"&gt;--pwned&lt;/span&gt; 1000000

&lt;span class="c"&gt;# 20 threads: ~30 seconds&lt;/span&gt;
python3 pwnagotchi-gen.py &lt;span class="nt"&gt;--count&lt;/span&gt; 1 &lt;span class="nt"&gt;--name&lt;/span&gt; mega &lt;span class="nt"&gt;--pwned&lt;/span&gt; 1000000 &lt;span class="nt"&gt;--threads&lt;/span&gt; 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pwned Count&lt;/th&gt;
&lt;th&gt;Threads&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Discovery&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;~8 min&lt;/td&gt;
&lt;td&gt;Connection timeout issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;~60 sec&lt;/td&gt;
&lt;td&gt;Improved but still slow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;~30 sec&lt;/td&gt;
&lt;td&gt;Optimal performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;~12 sec&lt;/td&gt;
&lt;td&gt;Diminishing returns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Rate Limiting Discovery
&lt;/h3&gt;

&lt;p&gt;By gradually increasing request volume, I found the grid has soft rate limits around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;50 concurrent connections per IP&lt;/li&gt;
&lt;li&gt;~1000 AP reports per minute per unit&lt;/li&gt;
&lt;li&gt;Exponential backoff on repeated failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding these limits helped design respectful testing that won't impact production grids.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protocol Edge Cases and Security
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Edge Case: Unicode SSIDs
&lt;/h3&gt;

&lt;p&gt;Testing revealed interesting handling of non-ASCII SSIDs:&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;# These all needed proper handling:
&lt;/span&gt;&lt;span class="n"&gt;ssid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;café&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;           &lt;span class="c1"&gt;# UTF-8 encoding
&lt;/span&gt;&lt;span class="n"&gt;ssid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;测试网络&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;        &lt;span class="c1"&gt;# Chinese characters  
&lt;/span&gt;&lt;span class="n"&gt;ssid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🔥WiFi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;         &lt;span class="c1"&gt;# Emoji (yes, really)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The grid properly handles UTF-8 encoding, but URL encoding in API calls required careful attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  Edge Case: Signature Replay
&lt;/h3&gt;

&lt;p&gt;What happens if you reuse a signature? Testing showed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signatures are time-sensitive (implicitly via JWT)&lt;/li&gt;
&lt;li&gt;But the enrollment signature can be cached&lt;/li&gt;
&lt;li&gt;Token refresh is handled gracefully&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Edge Case: Extreme Statistics
&lt;/h3&gt;

&lt;p&gt;Can units report billions of epochs or impossible uptime? Testing revealed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server validates reasonable ranges&lt;/li&gt;
&lt;li&gt;But allows for very high-uptime units&lt;/li&gt;
&lt;li&gt;Statistics are stored as appropriate integer types&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building the Testing Framework
&lt;/h2&gt;

&lt;p&gt;Understanding the protocol enabled building a comprehensive testing suite:&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentic Simulation
&lt;/h3&gt;

&lt;p&gt;Every synthetic unit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates real RSA-2048 keypairs&lt;/li&gt;
&lt;li&gt;Computes proper SHA-256 fingerprints
&lt;/li&gt;
&lt;li&gt;Signs enrollment exactly like real devices&lt;/li&gt;
&lt;li&gt;Cannot be distinguished from genuine Pwnagotchi units&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Fleet Management
&lt;/h3&gt;

&lt;p&gt;The CLI and web interface provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time monitoring of synthetic units&lt;/li&gt;
&lt;li&gt;Bulk operations (boot all, stop all)&lt;/li&gt;
&lt;li&gt;Per-unit Tor circuit management&lt;/li&gt;
&lt;li&gt;Persistent state across restarts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance Testing
&lt;/h3&gt;

&lt;p&gt;Multi-threading enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High-volume AP reporting (millions of networks)&lt;/li&gt;
&lt;li&gt;Concurrent unit enrollment&lt;/li&gt;
&lt;li&gt;Database connection pool stress testing&lt;/li&gt;
&lt;li&gt;API rate limit verification&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Applications
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Security Research
&lt;/h3&gt;

&lt;p&gt;Understanding the authentication protocol helps identify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Potential attack vectors (all mitigated by design)&lt;/li&gt;
&lt;li&gt;Rate limiting effectiveness&lt;/li&gt;
&lt;li&gt;DoS resistance under load&lt;/li&gt;
&lt;li&gt;Token handling security&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Protocol Documentation
&lt;/h3&gt;

&lt;p&gt;This project serves as unofficial documentation for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enrollment flow and requirements&lt;/li&gt;
&lt;li&gt;AP reporting format and validation&lt;/li&gt;
&lt;li&gt;Expected HTTP headers and responses&lt;/li&gt;
&lt;li&gt;Error handling and retry logic&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Grid Deployment Testing
&lt;/h3&gt;

&lt;p&gt;Before deploying your own opwngrid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test database performance under load&lt;/li&gt;
&lt;li&gt;Verify API server scaling&lt;/li&gt;
&lt;li&gt;Validate connection pooling&lt;/li&gt;
&lt;li&gt;Confirm rate limiting works&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical Deep Dive
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Authentication Internals
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Key generation
&lt;/span&gt;&lt;span class="n"&gt;private_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rsa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_private_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;public_exponent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;65537&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;key_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;default_backend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Fingerprint computation
&lt;/span&gt;&lt;span class="n"&gt;public_der&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;public_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;public_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;serialization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;serialization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PublicFormat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubjectPublicKeyInfo&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fingerprint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;public_der&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Signature generation
&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;@&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fingerprint&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="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;mgf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MGF1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="n"&gt;salt_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PSS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MAX_LENGTH&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;hashes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AP Reporting Strategy
&lt;/h3&gt;

&lt;p&gt;Intelligent sampling based on total count:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_sample_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_pwned&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total_pwned&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total_pwned&lt;/span&gt;  &lt;span class="c1"&gt;# Report all
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;total_pwned&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_pwned&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Up to 500
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;total_pwned&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_pwned&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 1%
&lt;/span&gt;    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_pwned&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.005&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# 0.5%, max 5K
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This maintains statistical accuracy while respecting API limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation &amp;amp; Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/4ngel2769/pwnagotchi-generator.git
&lt;span class="nb"&gt;cd &lt;/span&gt;pwnagotchi-generator
pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generate Test Units
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a test unit with realistic stats&lt;/span&gt;
python3 pwnagotchi-gen.py &lt;span class="nt"&gt;--count&lt;/span&gt; 1 &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--pwned&lt;/span&gt; 10000 &lt;span class="nt"&gt;--yes&lt;/span&gt;

&lt;span class="c"&gt;# Create multiple units through Tor&lt;/span&gt;
python3 pwnagotchi-gen.py &lt;span class="nt"&gt;--count&lt;/span&gt; 10 &lt;span class="nt"&gt;--tor&lt;/span&gt; &lt;span class="nt"&gt;--pwned&lt;/span&gt; random

&lt;span class="c"&gt;# High-performance testing&lt;/span&gt;
python3 pwnagotchi-gen.py &lt;span class="nt"&gt;--count&lt;/span&gt; 1 &lt;span class="nt"&gt;--name&lt;/span&gt; mega &lt;span class="nt"&gt;--pwned&lt;/span&gt; 1000000 &lt;span class="nt"&gt;--threads&lt;/span&gt; 20 &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fleet Management
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Launch CLI interface&lt;/span&gt;
python3 pwnie-manager.py

&lt;span class="c"&gt;# Or use the web dashboard&lt;/span&gt;
python3 pwnie-manager.py &lt;span class="nt"&gt;--webui&lt;/span&gt;
&lt;span class="c"&gt;# Browse to http://localhost:5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Protocol Design Insights
&lt;/h3&gt;

&lt;p&gt;The opwngrid protocol is well-designed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proper cryptographic authentication prevents impersonation&lt;/li&gt;
&lt;li&gt;JWT tokens enable stateless validation&lt;/li&gt;
&lt;li&gt;Rate limiting protects against abuse&lt;/li&gt;
&lt;li&gt;Graceful error handling aids debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reverse Engineering Ethics
&lt;/h3&gt;

&lt;p&gt;This project demonstrates responsible reverse engineering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose&lt;/strong&gt;: Build testing tools for legitimate infrastructure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: Share knowledge to help the ecosystem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respect&lt;/strong&gt;: Follow rate limits and don't abuse production grids&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Source&lt;/strong&gt;: All code is public for review and improvement&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance Optimization
&lt;/h3&gt;

&lt;p&gt;Key optimizations discovered through testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thread pooling dramatically improves throughput&lt;/li&gt;
&lt;li&gt;Intelligent sampling maintains accuracy&lt;/li&gt;
&lt;li&gt;Tor circuit reuse reduces overhead&lt;/li&gt;
&lt;li&gt;State persistence enables resumability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future Enhancements
&lt;/h2&gt;

&lt;p&gt;Planned improvements include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enhanced web dashboard with analytics&lt;/li&gt;
&lt;li&gt;Real-time log streaming&lt;/li&gt;
&lt;li&gt;Statistical analysis and graphing&lt;/li&gt;
&lt;li&gt;Configuration templates and profiles&lt;/li&gt;
&lt;li&gt;SQLite persistence for historical data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See &lt;a href="https://github.com/4ngel2769/pwnagotchi-generator/blob/main/readmes/WEB-DASHBOARD-PLAN.md" rel="noopener noreferrer"&gt;&lt;code&gt;readmes/WEB-DASHBOARD-PLAN.md&lt;/code&gt;&lt;/a&gt; for detailed roadmap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Reverse engineering opwngrid wasn't about breaking it - it was about understanding it deeply enough to build proper testing tools. The authentication is solid, the protocol is well-designed, and stress testing reveals how robust the system really is.&lt;/p&gt;

&lt;p&gt;Whether you're deploying your own grid, developing new features, or studying distributed system security, understanding the protocol at this level is invaluable. The Generator provides a practical tool while demonstrating responsible reverse engineering practices.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Want to explore the protocol yourself?&lt;/strong&gt; Check out the &lt;a href="https://github.com/4ngel2769/pwnagotchi-generator" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and start experimenting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Found this useful?&lt;/strong&gt; &lt;a href="https://github.com/4ngel2769/pwnagotchi-generator/star" rel="noopener noreferrer"&gt;Star the repo&lt;/a&gt; and share your findings with the community!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Always use this tool responsibly against your own infrastructure or with explicit permission. Respect rate limits and don't abuse public grids.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
