index.html (21669B)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="utf-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1"> 6 <title>onlyn00bs badge</title> 7 <meta name="description" content="OnlyN00bs — a DEF CON friend-finder badge. Setup, screens, battery safety, and what it broadcasts."> 8 <meta name="color-scheme" content="dark light"> 9 <meta property="og:title" content="onlyn00bs badge"> 10 <meta property="og:description" content="A DEF CON friend-finder badge. Setup, screens, battery safety, and what it broadcasts."> 11 <meta property="og:type" content="website"> 12 <meta property="og:site_name" content="virtualshack"> 13 <meta property="og:url" content="https://badge.virtualshack.io/"> 14 <!-- og:image MUST be an absolute URL — Discord, Slack and Twitter all refuse to resolve a 15 relative one, and the card silently renders with no image. 1200x630 (1.91:1) is the 16 shape those unfurlers crop to; feeding them the raw 4:3 photo means each platform 17 picks its own crop. --> 18 <meta property="og:image" content="https://badge.virtualshack.io/assets/img/og-badge.jpg"> 19 <meta property="og:image:type" content="image/jpeg"> 20 <meta property="og:image:width" content="1200"> 21 <meta property="og:image:height" content="630"> 22 <meta property="og:image:alt" content="Seven OnlyN00bs badges in coloured 3D-printed cases, screens lit showing the finder, points, report, best-friends, home and battery views."> 23 <!-- Without summary_large_image, Twitter/X and some clients render a small square thumbnail 24 instead of the wide card. --> 25 <meta name="twitter:card" content="summary_large_image"> 26 <meta name="twitter:title" content="onlyn00bs badge"> 27 <meta name="twitter:description" content="A DEF CON friend-finder badge. Setup, screens, battery safety, and what it broadcasts."> 28 <meta name="twitter:image" content="https://badge.virtualshack.io/assets/img/og-badge.jpg"> 29 <!-- ?v= must match CACHE in sw.js — bump BOTH on any content change. index.html is served 30 no-cache but /assets/ is not, and Cloudflare stretches it further (origin says 31 max-age=3600, the edge served 14400), so without a version in the URL a CSS change is 32 invisible to visitors for hours after a deploy. A new query = a new edge cache key = 33 it lands immediately. Safe for offline: sw.js matches with ignoreSearch, so this still 34 resolves to the precached bare URL. --> 35 <link rel="stylesheet" href="assets/badge.css?v=39"> 36 <link rel="icon" type="image/svg+xml" href="assets/icon.svg"> 37 <link rel="manifest" href="manifest.webmanifest"> 38 <meta name="theme-color" content="#212121"> 39 </head> 40 <body> 41 <main class="wrapper" id="top"> 42 43 <h1>// onlyn00bs</h1> 44 <p class="tagline">a friend-finder badge for def con 34 · firmware v1.10</p> 45 <p class="small muted"><a href="https://virtualshack.io/">virtualshack.io</a> / badge</p> 46 47 <!-- generated from the gallery manifest: the first row, once there are photos --> 48 <!-- gallery-preview:begin --> 49 <h2 id="photos">// photos</h2> 50 <div class="gallery gallery-preview"> 51 <figure> 52 <a href="gallery/" data-w="1600" data-h="1200"><img src="assets/gallery/thumbs/dc34-01.jpg" alt="A green OnlyN00bs badge on the finder screen beside the official DEF CON 34 badge and a challenge coin, with a purple foot charm on the case" 53 width="720" height="540" loading="eager" decoding="async"></a> 54 </figure> 55 <figure> 56 <a href="gallery/" data-w="1234" data-h="929"><img src="assets/gallery/thumbs/dc34-02.jpg" alt="A hand holding a purple OnlyN00bs badge on the finder screen, with a green badge on a lanyard behind it" 57 width="720" height="542" loading="eager" decoding="async"></a> 58 </figure> 59 <figure> 60 <a href="gallery/" data-w="929" data-h="1238"><img src="assets/gallery/thumbs/dc34-03.jpg" alt="A lavender badge with foot and gummy-bear charms, its finder screen listing five nearby badges" 61 width="540" height="720" loading="eager" decoding="async"></a> 62 </figure> 63 </div> 64 <p class="small"><a href="gallery/">all photos →</a></p> 65 <!-- gallery-preview:end --> 66 67 <p style="text-align:center"><a class="cta" href="provision/">Set up my badge →</a></p> 68 69 <!-- Changelog. Newest first, CAPPED AT THE 3 MOST RECENT — add to the top and drop the 70 bottom entry, so this stays a glance and never grows into a wall. Older entries live 71 in git history, not here. Badge changes and page corrections both land in it, because 72 from a reader's side "the badge does something new" and "this page was wrong" are the 73 same question: is what I read last time still true? Keep entries to one line and 74 user-facing (what changed for them, not which commit did it). --> 75 <div class="note log"> 76 <p><b>What's new</b> <span class="small muted">— newest first</span></p> 77 <ul class="small"> 78 <li><b>2026-09-26</b> · <b>The badge is open source</b> — firmware, setup app, this site, 79 and the case models. <a href="#source">Source + license</a>.</li> 80 <li><b>2026-08-05</b> · New <b>Deep sleep</b> feature — put the badge into low power mode without unplugging the 81 battery. <code>Badge Settings → Power → Deep sleep</code>, then press the button to 82 wake it. <b>Reboot has moved into the same Power menu.</b> Badges flashed before 83 2026-08-05 need a firmware update to get it.</li> 84 <li><b>2026-08-01</b> · <b>The setup app is live on this site</b> — hit 85 <a href="provision/">set up my badge</a> and go. Once you've loaded the PWA, it will 86 remain available for offline browser use.</li> 87 </ul> 88 </div> 89 90 <nav class="toc"> 91 <ul> 92 <li><a href="#what">// the concept</a></li> 93 <li><a href="#hardware">// the hardware</a></li> 94 <li><a href="#software">// the firmware</a></li> 95 <li><a href="#case">// the case, lanyard, & accessories</a></li> 96 <li><a href="#source">// the source</a></li> 97 <!-- the link appears once the gallery has photos --> 98 <!-- gallery-link:begin --> 99 <li><a href="gallery/">// photos</a></li> 100 <!-- gallery-link:end --> 101 <li><a href="#setup">// setup</a></li> 102 <li><a href="#screens">// usage</a></li> 103 <li><a href="#button">// the button</a></li> 104 <li><a href="#battery">// battery & safety</a></li> 105 <li><a href="#trouble">// troubleshooting</a></li> 106 <li><a href="#privacy">// privacy</a></li> 107 </ul> 108 </nav> 109 110 <h2 id="what">// the concept</h2> 111 112 <p>OnlyN00bs is not just an amateur badge project for DefCon 34, it's an amateur badge project for DefCon 34 brought to life through relentless tokenmaxxing. Could anything be more appropriate for 2026?</p> 113 114 <p>Each badge scans for fellow n00bs and feet enthusiasts, displaying real-time proximity information and beaming your own handle - and ASCII avatar - to those in range. Avatars are user-customizable — faces, name, the lot. Earn made up points. Earn even more made up points by being in range of your friends.</p> 115 116 <p>Designed to be at least mildly secure, every beacon your badge sends and receives over ESP-NOW is HMAC-signed.</p> 117 118 <h2 id="hardware">// the hardware (tl;dr)</h2> 119 <ul> 120 <li>ESP32-E (tariffs oof)</li> 121 <li>2.42" SSD1309 OLED (blue, yellow, and green available in limited quantities)</li> 122 <li>2000 mAh LiPo battery (12-14 hours per charge observed in testing)</li> 123 <li>USB-C charging (🫡 🇪🇺 )</li> 124 <li>A single button</li> 125 </ul> 126 127 <h2 id="software">// the firmware (tl;dr)</h2> 128 <li>"Secure" communications using "military-grade encryption" (a.k.a. HMAC signing)</li> 129 <ul><li><p class="small">Currently clean on OpSec 👊 🇺🇸🔥</p></li></ul> 130 <li>Beyond simple friend radar, badges track encounters over time, best friend(s), and more.</li> 131 <ul><li>Points are awarded for time spent near fellow badge-holders, with group bonus multipliers</li></ul> 132 <li>Customizable integrated virtual ASCII avatar that celebrates milestones, notifies you of low battery (and when to shower), and more</li> 133 <li>Points-history graph with multiple views</li> 134 <li>Battery gauge (a little rough) and storage remaining gauge</li> 135 <li>Fully wireless BLE provisioning via PWA, on-screen pairing code to deter casual interlopers</li> 136 <li>Points and encounter data export for end-of-Con visualizations (currently in development)</li> 137 138 <h2 id="case">// the case</h2> 139 <p>Each badge comes in a 3D printed glow-in-the-dark PETG plastic case. There are four primary color options: green, blue, pink (purple?), and multicolor.</p> 140 <p class="small">Note: ignore the lanyard in this photo, it was used for testing.</p> 141 142 <a class="photo" href="assets/img/all_colors.jpg" target="_blank" rel="noopener"> 143 <img src="assets/img/thumbs/all_colors.jpg" alt="the four case colours side by side: green, blue, pink and multicolour" 144 width="900" height="677" loading="lazy" decoding="async"></a> 145 146 <p>In order of glowieness ("luminescence" if you're snooty), the green is the brightest, followed by blue/multicolor, and pink:</p> 147 148 <div class="shots"> 149 <a class="photo" href="assets/img/glow_1.jpg" target="_blank" rel="noopener"> 150 <img src="assets/img/thumbs/glow_1.jpg" alt="cases glowing in the dark, first comparison shot" 151 width="677" height="900" loading="lazy" decoding="async"></a> 152 <a class="photo" href="assets/img/glow_2.jpg" target="_blank" rel="noopener"> 153 <img src="assets/img/thumbs/glow_2.jpg" alt="cases glowing in the dark, second comparison shot" 154 width="677" height="900" loading="lazy" decoding="async"></a> 155 </div> 156 <h3 id="lanyard">// the lanyard</h3> 157 <p>Each badge will come with a lanyard:</p> 158 <a class="photo" href="assets/img/lanyard.jpg" target="_blank" rel="noopener"> 159 <img src="assets/img/thumbs/lanyard.jpg" alt="the lanyard that ships with each badge" 160 width="900" height="675" loading="lazy" decoding="async"></a> 161 <h3 id="accessories">// the accessories</h3> 162 <p>The two openings in the case were designed to be backwards compatible with DC33 OnlyFeet/N00b_Sec Croc jibbitz (and the rest of <a href="https://www.crocs.com/c/jibbitz" target="_blank" rel="noopener">Crocs Jibbitz</a> ecosystem... bring 'em if ya got 'em.). Equally, you can buy them from Crocs, 3D print them, and/or buy them from <a href="https://www.etsy.com/market/jibbitz" target="_blank" rel="noopener">Etsy</a>.</p> 163 <p>Additionally, a variety of 3D printed accessories (e.g., as seen in the photo above) will be provided for customization. These are still in development (but if you have an idea for something really funny, let me know.):</p> 164 <a class="photo" href="assets/img/accessories.jpg" target="_blank" rel="noopener"> 165 <img src="assets/img/thumbs/accessories.jpg" alt="3D-printed accessories that clip into the case openings" 166 width="900" height="675" loading="lazy" decoding="async"></a> 167 168 <h2 id="source">// the source</h2> 169 <p>The badge is open source: the firmware, the setup app, this site, the parts list, 170 and the parametric case (STLs included, so you can print one without touching CAD).</p> 171 <p style="text-align:center"><a class="cta" href="https://git.virtualshack.io/onlyn00bs-badge/">Browse the source →</a></p> 172 <p class="small muted" style="text-align:center"><code>git clone https://git.virtualshack.io/onlyn00bs-badge.git</code> · <a href="https://git.virtualshack.io/onlyn00bs-badge/releases/">releases</a></p> 173 <ul> 174 <li><b>License:</b> MIT for the code; CC BY-SA 4.0 for the case models, docs and photos.</li> 175 <li><b>Not included: the group key.</b> Badges only see badges signed with the same key, so 176 build your own crew with your own key and it won't talk to ours. The README says how.</li> 177 <li><b>Also not included: anyone's data.</b> Encounter logs and the list of who got which badge 178 stayed private. The repo is the badge, not the con.</li> 179 </ul> 180 181 <h2 id="setup">// setup</h2> 182 183 <p>You need: the badge, a computer (with <b>Chrome or Edge</b>) or an Android phone, and about two 184 minutes. <b>Not an iPhone</b>: setup uses Web Bluetooth, which doesn't exist on iOS in any 185 browser.</p> 186 187 <ol> 188 <li><b>Put the badge in setup mode.</b> From the badge: <code>OPTIONS → Badge Settings → 189 Developer Options → Provisioning Mode</code>. (You can also hold the microcontroller's 190 <code>BOOT</code> button while powering it on — but that means opening the case, so the 191 menu is the easier route.)</li> 192 <li><b>Read the 3-character pairing code</b> off the badge screen (something like 193 <code>K7Q</code>). This stops strangers from reconfiguring your badge.</li> 194 <li><b>Open <a href="provision/">the setup app</a></b>, type in your handle, the pairing code, optionally configure an avatar, then hit <em>connect & save</em>.</li> 195 <li><b>Select your badge</b> from the browser's Bluetooth pop-up. It will show up as 196 <code>badge-setup-<MAC></code>. The badge then reboots into normal mode with 197 your handle set, and its internal clock (used to timestamp your encounters — it isn't 198 shown on screen) set from your device.</li> 199 </ol> 200 <div class="note stop"><p>If that internal clock drifts after a battery swap — your con REPORT 201 times go "relative" — the same web app has a <em>sync clock only</em> button that re-syncs it 202 without touching your handle.</p> 203 </div> 204 205 <div class="note warn"> 206 <p><b>Stuck in setup mode? Hold the badge's button for 5 seconds.</b> A badge in setup mode is 207 not beaconing and won't see anyone, and will sit there forever. Holding the button 208 counts down on screen and reboots straight to HOME.</p> 209 </div> 210 <p class="small">Note: A badge that has never been set up will name itself something like 211 <code>n00b-1858</code> on the way out, so that it works immediately.</p> 212 <h2 id="screens">// usage</h2> 213 214 <p>Seven views, in a loop. Double-tap the button to walk forward through them:</p> 215 216 <pre><code> double-tap walks down this list a single tap here does… 217 ┌────────────────────────────────────────────────────────────── 218 │ HOME handle, avatar, points, battery 219 │ FINDER who's in range, closest first page the list 220 │ REPORT your con so far 221 │ POINTS points over time, as a graph cycle 5d / 24h / 1h 222 │ FRIENDS best-friends ledger 223 │ BATTERY charge level flip to storage 224 │ OPTIONS settings move the cursor 225 └── and wraps back to HOME ──────────────────────────────────── 226 227 hold the button 1.5s from anywhere → jump straight to HOME 228 </code></pre> 229 230 <dl> 231 <dt>HOME</dt> 232 <dd>Your handle, your avatar, total points, and battery. The default view.</dd> 233 234 <dt>FINDER</dt> 235 <dd>Who's in range right now, closest first. <b>Tap</b> to page through the list if 236 there are more badges than fit. A badge drops off ~15 seconds after its last 237 beacon.</dd> 238 239 <dt>REPORT</dt> 240 <dd>Your con so far: an encounter summary, snapshotted when you arrive on the 241 screen.</dd> 242 243 <dt>POINTS</dt> 244 <dd>Points over time as a graph. <b>Tap</b> to cycle the window: 5 days / 24 hours / 245 1 hour.</dd> 246 247 <dt>FRIENDS</dt> 248 <dd>Your best-friends ledger — who you've actually spent time near, ranked by shared 249 minutes.</dd> 250 251 <dt>BATTERY</dt> 252 <dd>Live battery gauge. <b>Tap</b> to flip to the storage readout (how full the 253 badge's flash is).</dd> 254 255 <dt>OPTIONS</dt> 256 <dd>Note: In OPTIONS, <b>tap</b> moves the cursor down a row, 257 <b>double-tap</b> selects it (and double-taps back out).</dd> 258 </dl> 259 260 <h2 id="button">// the button</h2> 261 262 <p>One button, three gestures:</p> 263 264 <pre><code> tap → do this screen's thing (page the list, cycle the graph…) 265 tap tap → next screen 266 hold 1.5s → jump back to HOME (works from anywhere) 267 268 in SETUP MODE only: 269 hold 5s → leave setup, reboot to HOME 270 </code></pre> 271 272 <p>If you're ever lost, hold the button. You'll land on HOME.</p> 273 274 <h2 id="battery">// battery & safety</h2> 275 276 <p>It's a lithium-polymer pouch cell and we're in the desert. Do the math.</p> 277 278 <ul> 279 <li><b>Tested runtime:</b> about <b>14 hours</b>. Bring a power bank or charge nightly.</li> 280 <li><b>Remember</b> that LiPo batteries left empty for more than a few hours can be permanently damaged.</li> 281 <li><b>Charging:</b> via USB-C, roughly <b>4 hours</b> from empty to full. Charge it on a 282 hard surface — not on a hotel bed, a couch, or buried in a backpack.</li> 283 <li><b>Never</b> charge a pack that is hot, puffy, swollen, or has been crushed or 284 punctured. A swollen pouch is done; stop using it. <b>I'll give you a new one for free.</b></li> 285 <li><b>Never leave it in a parked car</b>, on a dashboard, or in direct sun. A closed car 286 in Vegas will cook a pouch cell well past anything it's rated for.</li> 287 <li>Don't leave it charging unattended.</li> 288 <li><b>Deep sleep is not "off":</b> a sleeping badge still draws a (very) small amount of power and will die/kill the battery eventually. For longer periods (8+ hours), charge it to roughly half and unplug the battery at the JST connector.</li> 289 <li>Dead or damaged cells go to battery-recycling (please don't toss them in the trash).</li> 290 </ul> 291 292 <h2 id="trouble">// troubleshooting</h2> 293 294 <dl> 295 <dt>The setup page has no "connect" button, or the button does nothing</dt> 296 <dd>You're on a browser without Web Bluetooth. Use Chrome or Edge on a laptop or 297 Android. iOS cannot do this at all — I wasn't lying before lol.</dd> 298 299 <dt>FINDER shows nobody</dt> 300 <dd>In order of likelihood: (1) nobody is actually in range — the radio range is in the tens 301 of meters (and bodies absorb 2.4 GHz), (2) the other badge is in setup 302 mode, so it isn't beaconing, (3) mismatched builds — if multiple people see 303 nothing, compare firmware versions on <code>OPTIONS → Whoami</code>.</dd> 304 305 <dt>My REPORT times look wrong or say "relative"</dt> 306 <dd>There's no clock on screen, but the badge still keeps time internally to timestamp your 307 encounters — and it has no battery-backed clock, so it gets the time from your phone or 308 laptop during setup. After a battery swap that can drift; the REPORT view then shows 309 <em>relative</em> times instead of real ones. Run <em>sync clock only</em> in the setup app 310 to fix it. Don't do this on an iPhone-only trip — you can't (sensing the theme yet?).</dd> 311 312 <dt>I'm stuck on the SETUP MODE screen</dt> 313 <dd><b>Hold the button for 5 seconds.</b> The screen counts down and the badge reboots 314 to HOME. Works with no app and no network. One catch: it wants a <em>fresh</em> press, 315 so if you got here by holding <code>BOOT</code> through power-on, let go first, then 316 hold again.</dd> 317 318 <dt>My badge won't connect over Bluetooth</dt> 319 <dd>It only advertises while in setup mode. If the browser's chooser is empty, the 320 badge isn't in setup mode — power-cycle it holding <code>BOOT</code>.</dd> 321 322 <dt>It says the pairing code is wrong</dt> 323 <dd>The code is 3 characters shown on the badge screen and it changes each time the 324 badge enters setup mode (the charset deliberately has no 325 <code>0/O</code> or <code>1/I/L</code>).</dd> 326 327 <dt>The badge says <code>locked, power-cycle</code></dt> 328 <dd><b>Five wrong pairing codes and the badge shuts its setup gate until it reboots</b> 329 — that's on purpose, so nobody can sit in range guessing codes at your badge. Power- 330 cycle it (hold <code>BOOT</code> if you want to go straight back into setup) and 331 you'll get a fresh code.</dd> 332 </dl> 333 334 <h2 id="privacy">// privacy</h2> 335 336 <p>It is a radio and DEF CON is full of weird people listening to radios.</p> 337 338 <ul> 339 <li>Your badge broadcasts <b>your handle in cleartext, once per second</b>, plus a 340 stable hardware address that doesn't change.</li> 341 <li>The HMAC signature proves a beacon came from someone with the group key. It 342 <b>signs, it does not encrypt</b> — anyone sniffing 2.4 GHz nearby can read the 343 handles and follow a badge around the venue.</li> 344 <li>So: <b>pick a handle you're happy having in the air</b>, and assume presence is 345 public.</li> 346 <li>If you'd rather not be seen, <b>Badge Settings → Stealth</b> turns your broadcast 347 off. You can still see other badges; they can't see you.</li> 348 <li>Encounter logs, points, and your best-friends list stay on the badge. Nothing is 349 uploaded anywhere — the setup app talks only to the badge in front of you, and the 350 metrics export for post-con visualizations runs in your browser.</li> 351 <li><b>Nobody can reconfigure your badge over the air.</b> Setup only listens while the 352 badge is in setup mode.</li> 353 </ul> 354 355 <p style="text-align:center"><a class="totop" href="#top">↑ back to top</a></p> 356 357 <footer style="text-align:center"> 358 <p>onlyn00bs · def con 34<br/>lab grown in <a href="https://virtualshack.io/">virtualtack's virtualshack</a> with some help from Claude</p><br/> 359 <p class="small">If something here is wrong or 360 missing, please let me know.</p> 361 </footer> 362 363 </main> 364 <script> 365 if ("serviceWorker" in navigator) { 366 addEventListener("load", () => navigator.serviceWorker.register("sw.js").catch(() => {})); 367 } 368 </script> 369 </body> 370 </html>