Redditor "Ordo_Liberal" recently claimed to have won back their compromised Microsoft account by taking the tech giant to court. The episode offers a useful playbook for dealing with the company's customer support, and serves as a warning as gaming companies phase out physical media.
Hey everyone! I was working on a Node.js project and realized I needed a better way to handle data models. I built and just published justwork-model-ts.It's built specifically for TypeScript and helps structure backend database schemas cleanly. You can grab it directly from the marketplace here:
https://marketplace.visualstudio.com/items?itemName=EllaAndy.justwork-model-ts
Would love any feedback, feature requests, or code reviews!
Published: Sun, 12 Jul 2026 17:19:48 +0000 | Source: DEV Community
The flag format is given as
bronco{XXXX...}
. Nothing else. This is an
OSINT / web-recon style challenge: the flag is broken into 8 numbered
fragments, scattered across the live site using a mix of static HTML,
downloadable files, and client-side JavaScript behavior. The goal is to
find all 8 pieces and assemble them in order.
Recon
Fetching the page's rendered HTML directly gives a first look. Most of
the page is a fairly standard "CTF landing page" — hero banner, stats
counters, sponsor logo, footer credits. Scanning through it for anything
that looks out of place (odd
alt
text,
title
attributes, query
strings, filenames) turns up four fragments immediately, sitting in
plain sight in the static markup:
#
Fragment
Location
1
bronco{h
Plain text at the very end of the page footer, after the credits
3
0und_th3
title
attribute on the "Join the Competition" button/link
6
ut31y_n0
Query string on the "BroncoCTF 2026...?" repository card:
href="/BroncoCTF?KEY=6-ut31y_n0"
8
_4t_411}
alt
text on the sixth stats card (the one using
correct-flag-colorized.svg
)
That's 4 of 8, found just by reading the rendered HTML carefully.
The word "2026" is a hyperlink to
/7.txt
, disguised as normal body
text (no obvious styling gives it away as a link in the rendered page).
Fetching it directly:
$curl https://broncosec.com/7.txt
7 - _w0rr135
Piece 7 found.
Pieces 2, 4, 5 — hidden in client-side JavaScript
The remaining three fragments never appear in the static HTML at all —
they're generated at runtime by JavaScript event handlers, meaning a
plain
curl
/fetch of the page will never reveal them. The fix is to
pull down the site's actual JS bundles and grep them directly.
Step 1 — enumerate the script chunks.
Looking at the
<script src=...>
tags in the raw page source (Next.js app, so chunks are hashed filenames
under
/_next/static/chunks/
):
$curl -sO https://broncosec.com/_next/static/chunks/e785679bf8074938.js
$curl -sO https://broncosec.com/_next/static/chunks/f31cf569852813cb.js
... (and the rest of the referenced chunks)
Step 2 — grep for anything flag-shaped:
$grep-rn"bronco{".$grep-rnE"[0-9]+ - ".
This immediately surfaces three
onClick
handlers and one DOM-injection
trick buried in the minified React component source:
Fragment 2 — hidden behind a click handler on the word "flags":
Clicking the word
"flags"
in the body text appends
2 - 3y_y0u_f
into a hidden
<div id="addtext">
on the page and plays a sound. A
static HTML fetch will never show this — it only exists after the click
event fires and mutates the DOM.
Clicking the raised-hand emoji under "Online Bragging Rights" sets a
cookie named
KEY4
containing the fragment, and appends a 🍪 emoji
next to it as visual confirmation.
Fragment 5 — hidden as a dynamically-injected HTML comment:
A placeholder
<script>
tag gets replaced, after mount, with an HTML
comment
containing the fragment. This only exists in the live DOM
after React hydrates — invisible both to a static curl and to
"view source", since browsers' "view source" shows the original
server-rendered HTML, not the post-hydration DOM. Only "Inspect
Element" (which reflects the live DOM) or a JS-aware fetch would catch
it.
Assembling the flag
Ordering all 8 recovered fragments by their embedded index:
Static fetches only show you half the picture on modern JS-framework
sites.
Anything gated behind an
onClick
, a
useEffect
, or other
client-side hydration logic is invisible to
curl
/basic
web_fetch
— you have to either pull and read the actual JS bundles, or interact
with a live, JS-executing browser (DevTools/Inspect Element) to see
DOM state that only exists after the page runs.
Downloading and grepping the full JS bundle set
(
/_next/static/chunks/*.js
for Next.js apps) is a fast, reliable way to recover client-side logic
and embedded strings without needing to manually trigger every
interaction in a browser first.
Minified React source is still greppable.
Even heavily minified
JSX compiles down to recognizable patterns (
onClick:
, string
literals, JSX attribute names) that survive minification well enough
to
grep
for target patterns like flag formats or numbered fragments.
Distinguish
"view source" vs. "Inspect Element"
: view-source shows
the original server response; Inspect Element (or any DOM-reading
tool) shows the current, JS-mutated state. Challenges that hide data
in post-hydration DOM changes require the latter.
Published: Sun, 12 Jul 2026 17:16:27 +0000 | Source: DEV Community
I built the
World Cup Rivalry Bot
— a Telegram bot for fans who want their World Cup rivalry to feel alive in their own group chat. You set your team and your rival, and whenever a real goal happens in the tournament, the bot fires back an AI-generated hype line in your team's voice — followed by an imagined clapback from the rival's fans, spoken in a completely different voice.
It works in DMs and in group chats, and each person in a group can root for their own team independently. Since World Cup passion isn't just about scoring — it's about the back-and-forth banter between rival fans — I wanted the bot to capture both sides of that rivalry, not just one team's celebration.
The bot is written in Go and runs as a single long-polling process against the Telegram Bot API, deployed on Railway.
A few decisions worth calling out:
Two voices, two AI models working together.
Every goal celebration is a two-part exchange: Google's
Gemini
generates a short hype/trash-talk line for whichever team scored, and a second, different clapback line from the other side's perspective. Each line is converted to speech with
ElevenLabs
, using two distinct voice IDs — one for the "home" team's fans, one for the rival's — so it genuinely sounds like two different people talking past each other, not one narrator switching sides.
Real tournament data, not mocked results.
The bot polls a public, free World Cup 2026 dataset to detect when a tracked team's goal total increases, and auto-fires the celebration — no manual trigger needed once
/autohype
is on. Since that data isn't second-by-second live, I added
/simulate
and
/simulaterival
so the exact same detection-and-celebrate code path can be demoed instantly, without waiting on an actual goal.
Per-user state, not per-chat.
Team and rival are tracked by Telegram user ID rather than chat ID, so a group of friends can all use the bot in the same group chat, each rooting for their own team, without stepping on each other's setup.
Getting the AI stack working was its own adventure.
Both
gemini-1.5-flash
and
gemini-2.0-flash
were already fully deprecated by the time I built this, and ElevenLabs had recently locked free-tier API access to preset "library" voices — so most of Saturday went into just finding which models and voices were actually still available before I could write a single hype line.
Prize Categories
Best Use of Google AI
— Gemini generates both the hype line and the rival's comeback line for every goal.
Best Use of ElevenLabs
— Two distinct ElevenLabs voices bring each side of the rivalry to life as actual voice notes in the chat.
Published: Sun, 12 Jul 2026 17:10:46 +0000 | Source: DEV Community
$ file bank
bank: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically
linked, interpreter /lib64/ld-linux-x86-64.so.2, not stripped
Running it presents a menu-driven "bank" simulator:
+------------------------------------------+
| BRONCO NATIONAL BANKv3.0 |
| "We take security seriously." |
+------------------------------------------+
Welcome! Starting balance: $100
Looking for the flag? It costs $1000000.
[1] Deposit(max $10000 per txn, 3 remaining)
[2] Withdraw
[3] Dispute a Charge (up to $1000000, one-time)
[4] Invest (fixed 0.5% return)
[5] Buy Flag ($1000000)
[6] Exit
Goal: get balance to
≥ $1,000,000
starting from
$100
, with deposits
capped at $10,000 × 3 = $30,000 total — nowhere near enough through
legitimate means. There must be a logic bug in one of the other options.
Recon
$ checksec --file=bank
RELRO STACK CANARYNXPIE
Partial RELRO No canary found NX disabled No PIE
No canary, NX disabled, no PIE — this binary
could
support classic stack
smashing / shellcode injection, but the binary is small, not stripped, and
has a suspiciously named function sitting right there:
$ nm bank | grep' T '
0000000000401223 T main
00000000004011d6 T win
A dedicated
win
function that isn't called from anywhere obvious in
normal program flow is the classic signature of a
"reach this function"
style challenge — no shellcode or ROP needed, just a logic bug that gets
main
to call
win()
on our behalf.
$ objdump -d --disassemble=win bank
00000000004011d6 <win>:
4011d6: endbr64
...
call puts@plt; prints first message
...
call puts@plt; prints second message (the flag)
ret
Confirmed:
win()
just prints two strings — almost certainly the flag.
The only remaining question is
what condition triggers a call to
win
.
This is the "Buy Flag" option's logic — straightforward, no funny business
here. The bug must be in how
balance
(stored at
-0x4(%rbp)
throughout
main
) can be inflated.
The vulnerable function: Dispute a Charge
Disassembling the "Dispute" branch (menu option 3):
; scanf reads dispute_amount into -0x18(%rbp)
4015a9: printf "Dispute amount: $"
4015bf: scanf "%d", &dispute_amount
4015e2: mov-0x18(%rbp), %eax ; eax = dispute_amount
4015e5: mov%eax, %edx
4015e7: neg%edx; edx = -dispute_amount
4015e9: cmovns %edx, %eax; eax = abs(dispute_amount) <-- bounds check uses ABS VALUE
4015ec: cmp$0xf423f, %eax; compare abs(amount) to 999,999
4015f1: jle401611; if abs(amount) <= 999,999 -> allowed
; 401611: this is the "allowed" path
401611: mov-0x18(%rbp), %eax ; eax = ORIGINAL dispute_amount (NOT abs!)
401614: add%eax, -0x4(%rbp); balance += dispute_amount
401617: movl $0x1, -0x8(%rbp); mark "already disputed" flag
Decompiled, the logic is equivalent to:
intdispute_amount;scanf("%d",&dispute_amount);if(abs(dispute_amount)<=999999){balance+=dispute_amount;// uses the ORIGINAL signed value, not the abs()already_disputed=1;}
The bugs
No verification of a prior charge.
"Dispute a Charge" implies you're
getting a refund for money you previously spent — but the function never
checks that any such charge exists. You can "dispute" an amount you
never paid.
Bounds check vs. effect mismatch.
The bounds check validates
abs(dispute_amount) <= 999999
, correctly rejecting anything with a
magnitude over that threshold in
either
direction. But the actual
balance update uses the
raw, signed
dispute_amount
— so a large
positive
value close to the cap sails through the check and gets
added directly to balance, with no requirement that it be tied to
real prior activity.
Combined, this means: enter
999999
as the dispute amount, and balance
jumps by nearly a million dollars, no strings attached.
Exploit
Starting balance is $100. A single dispute clears the flag threshold:
[3] Dispute a Charge (up to $1000000, one-time)
Dispute amount: $999999
[+] Dispute processed. Refund of $999999 applied.
New balance: $1000099
Balance is now
$1,000,099
— over the $999,999 threshold checked at
the "Buy Flag" call site.
[5] Buy Flag ($1000000)
This triggers:
if(balance>999999){win();// prints the flag}
No deposits, investing, or withdrawals are even necessary — a fresh run
straight into option 3 with
999999
is sufficient.
Root Cause Summary
Issue
Detail
Missing state validation
"Dispute" has no concept of a real prior charge to dispute against — it's an unconditional credit gated only by a magnitude check.
Check/effect mismatch
The bounds check computes
abs(amount)
but the balance update uses the original signed
amount
, so the "safety" check doesn't actually constrain what gets added.
One-time limit is irrelevant
The
already_disputed
flag prevents a
second
dispute, but a single dispute is already enough to win, so the limit provides no real protection.
Flag
Running the exploit path yields the flag output from
win()
.
Key Takeaways
When a binary is unstripped and has a suspiciously-named function like
win
, always check first whether it's reachable directly through some
input-driven logic bug before reaching for exploitation techniques like
ROP/shellcode — despite
NX disabled
and
no canary
hinting at that
path, this challenge didn't need it.
Always check that a
bounds/validation check
and the
operation it
guards
actually use the
same
value. Here, the check computed
abs(x)
but the effect used
x
directly — a very common class of bug
where sanitization is performed on a copy/derived value instead of the
value actually used downstream.
"Refund"/"dispute"/"credit" style features are worth extra scrutiny in
any financial-logic challenge — they're an intentional design pattern
for injecting value into a system, and are exactly where missing
state/history validation tends to hide.
Published: Sun, 12 Jul 2026 17:10:34 +0000 | Source: DEV Community
While learning core computer science subjects or development or any tech stack understanding algorithms, programming languages and mathematics is not the hardest part the hardest part is to stay consistent and stay sticked to the goal.
Why Am I Writing?
Like many aspiring developers, I've started countless courses, begun exciting projects, and promised myself I'd stay consistent with programming practice. But more often than I'd like to admit, I left them unfinished. I wasn't lacking interest—I was lacking consistency.
This time, I'm taking a different approach.
I've decided to document my entire learning journey publicly. Here, I'll share my progress, the lessons I learn, the mistakes I make, and the milestones I achieve. I'll also write blogs explaining computer science concepts, data structures and algorithms, mathematical theories, and the ideas behind the projects I build.
My hope is that writing about what I learn will keep me accountable, deepen my understanding, and maybe even help someone else who is on the same path.
What You Can Expect
Over the coming months, I'll be writing daily ,i will write minimum 1 blog dailway for the upcoming 200 days , about the topics I'm studying and the projects I'm building.
Some of the areas I'll cover include:
Data Structures and Algorithms
Core Computer Science subjects
Mathematics for Computer Science
Backend and Full-Stack Development
Data Analytics
Real-world development projects
Lessons learned from debugging and problem solving
Productivity, consistency, and my learning process
I'll will be using this is platform to create digital notes that will be easy to understand and will explain fundamental and complex topics of computer science.
I
believe in building in public
. Now,
I'm choosing to learn in public.
Every concept I master, every project I build, every mistake I make, and every lesson I learn will become a part of this journey.
This is Day 1—not of becoming an expert, but of refusing to quit.
See you in the next post.
Until then, keep learning, keep building, and keep showing up.
written by - AP09 - DAY 01
Published: Sun, 12 Jul 2026 17:08:43 +0000 | Source: DEV Community
📌 Introducción: El origen del problema (De una libreta a código)
El software más útil nace de resolver problemas reales cotidianos. Este proyecto comenzó cuando observé a un familiar que prestaba dinero de manera informal. Su sistema de gestión consistía en una libreta física y hojas de cálculo en Excel.
El resultado era caótico:
Olvidos frecuentes en las fechas de cobro.
Falta de claridad al calcular abonos parciales o intereses acumulados.
Horas perdidas redactando manualmente estados de cuenta por WhatsApp.
Para solucionar esto, decidí diseñar e implementar Si Presto!, una aplicación móvil nativa construida con Flutter y Firebase enfocada en el control de préstamos informales, optimizada para contextos de baja conectividad y con un modelo híbrido de monetización.
🏗️ La Arquitectura: Diseño de Software y Flujo de Datos
Para asegurar un rendimiento óptimo en dispositivos de cualquier gama, estructuré la aplicación bajo una Arquitectura Limpia (Clean Architecture) organizada en un Monorepo móvil con separación estricta de responsabilidades:
Toda la generación de código inmutable y reactivo se automatizó utilizando
build_runner
con herramientas como
@freezed
,
@riverpod
e
@Isar
.
🛡️ Los 4 Grandes Retos Técnicos de Ingeniería
1. Sincronización Offline-First y Consistencia de Datos en Venezuela
El Problema:
La aplicación debe funcionar sin fricciones en zonas con conectividad intermitente o nula. Si el cobrador registra un abono en la calle sin señal, los saldos locales no pueden duplicarse ni corromperse al recuperar la red.
La Solución:
Implementé un modelo Offline-First utilizando Isar Database como almacenamiento NoSQL local de alto rendimiento. La aplicación realiza todas las operaciones CRUD de forma síncrona en la base de datos local y, mediante un servicio de sincronización en segundo plano, respalda la información en la nube con Cloud Firestore únicamente si el usuario cuenta con conectividad y posee el plan Premium.
2. Generación Dinámica de PDF Nativos en Dispositivos de Gama Baja
El Problema:
Generar reportes financieros detallados consume memoria. Si el renderizado se realiza de forma ineficiente, los teléfonos de gama baja pueden sufrir cierres inesperados (crashes por OOM).
La Solución:
Diseñé un motor de renderizado transaccional utilizando los paquetes nativos
pdf
y
printing
. El motor procesa las colecciones inmutables de transacciones en bloques (chunks) directamente en la memoria caché del dispositivo, ensamblando el PDF en tiempo real para que el cobrador pueda exportarlo y compartirlo inmediatamente por WhatsApp sin sobrecargar el procesador del teléfono.
3. Modelo de Monetización Híbrido (AdMob + RevenueCat)
El Problema:
Mantener la aplicación requiere rentabilidad, pero saturar una herramienta financiera de cobros con anuncios intrusivos arruina por completo la experiencia de usuario (UX).
La Solución:
Implementé un esquema Freemium controlado minuciosamente desde la capa de datos:
Free Tier: Límites lógicos quemados en el repositorio (Máximo 3 deudores y 4 pagos por préstamo) financiados mediante banners publicitarios no intrusivos usando Google Mobile Ads.
Premium Tier: Integrado de manera limpia con el SDK de RevenueCat. Al detectar la compra activa mediante el singleton
PurchaseService
, la aplicación remueve por completo los anuncios, desbloquea deudores/pagos ilimitados, habilita la exportación a PDF y activa el respaldo en la nube con Cloud Firestore.
4. Automatización de Alertas y Notificaciones Locales
Para eliminar la dependencia constante de servidores externos (Push Notifications) que pueden fallar si el dispositivo no tiene datos móviles, configuré un servicio autónomo basado en
flutter_local_notifications
. La aplicación programa internamente recordatorios de cobro locales basados en las fechas límite de las entidades del dominio de datos, manteniendo al cobrador alerta sin consumir datos de red.
🎨 Identidad Visual: Estética Neobrutalista Oscura
Para mantener la coherencia con mi marca personal, la interfaz de Si Presto! adopta un estilo Neobrutalista Oscuro caracterizado por:
Un fondo oscuro profundo de alto contraste combinado con un verde neón eléctrico (
#00FF99
) para destacar los saldos positivos.
Tarjetas de resumen financiero con bordes marcados y sombras sólidas planas desplazadas, rompiendo con los diseños tradicionales y planos de las aplicaciones bancarias comunes.
🧠 Lecciones de Ingeniería de Producto
Construir
Si Presto
! partiendo de una necesidad familiar real me enseñó que la arquitectura de software senior no se mide por la cantidad de servicios en la nube que utilices, sino por saber delegar eficientemente el cómputo. Al elegir almacenar datos localmente con Isar, renderizar PDFs nativos en el cliente y programar notificaciones locales, reduje los costes de infraestructura de la aplicación a prácticamente cero dólares para el nivel gratuito, asegurando un producto escalable y altamente rentable.
We're given
lego_bricks_challenge.zip
, a 63 KB archive.
$zipinfo lego_bricks_challenge.zip
Archive:lego_bricks_challenge.zip
Zip file size: 63307 bytes, number of entries: 333
-rw----...lego_bricks_challenge\.DS_Store
-rw----...lego_bricks_challenge\.cache\sessions\.npmrc
-rw----...lego_bricks_challenge\.git\objects\pack\docker-compose.yml
-rw----...lego_bricks_challenge\src\modules\network\protocols\.solver.py
... (329 more decoy entries) ...
333 files, 4434 bytes uncompressed, 4627 bytes compressed
The listing reveals 333 files buried in a deep, decoy-heavy directory tree (
.cache
,
.config
,
.git
,
.local
,
data
,
docs
,
src
,
tmp
, etc.) — a classic haystack pattern. Almost every file is tiny (0–4 bytes) and named either generically (
Dockerfile
,
go.sum
,
config.ini
,
package.json
) or as
.part_NN
, with
NN
ranging well past 250. The file sizes and naming make it clear most of this is padding meant to bury the real content.
Recon
Sorting the extracted tree by file size immediately isolates the outlier:
Every other file is under 62 bytes;
.solver.py
at 906 bytes stands out by more than an order of magnitude — and it's hidden (dot-prefixed) inside an otherwise-decoy path.
Reading the solver
$ cat"lego_bricks_challenge/src/modules/network/protocols/.solver.py"#!/usr/bin/env python3"""Hidden solver for: LEt's a GO! concatenate .part_00 .. .part_49"""
import os, re, sys
def solve(start_dir: str) -> str:
pattern = re.compile(r"^\.part_(\d+)$")
parts: dict[int, str] ={}for dirpath, _, filenames in os.walk(start_dir):
for fn in filenames:
m = pattern.match(fn)if m:
idx = int(m.group(1))if 0 <= idx <= 49:
with open(os.path.join(dirpath, fn)) as fh:
parts[idx] = fh.read()
flag ="".join(parts[i] for i in sorted(parts))return flag
...
The script's docstring spells out the trick:
"Hidden solver for: LEt's a GO! concatenate
.part_00
..
.part_49
"
.
Its logic:
Walk the entire extracted tree.
Collect every file matching
^\.part_(\d+)$
whose index is between 0 and 49 inclusive (ignoring the hundreds of
.part_NNN
decoys with indices outside that range or non-matching names).
Concatenate their contents in ascending numeric order — the file's location in the directory tree is irrelevant, only the number in its name matters.
So the real flag is fragmented one character (or a few bytes) at a time across 50 files scattered throughout the tree, hidden among ~280 similarly-named but irrelevant decoy files.
Solving
The provided
.solver.py
itself couldn't be executed directly — it has a stray non-UTF-8 byte (a mangled emoji) in its docstring, which Python's parser chokes on:
SyntaxError: Non-UTF-8 code starting with '\x97' in file .../.solver.py on line 2
Rather than fix the encoding, we reimplemented the same logic cleanly, reading each part in binary mode to sidestep any encoding issues in the data itself:
#!/usr/bin/env python3
importos,re,sysdefsolve(start_dir:str)->bytes:pattern=re.compile(r"^\.part_(\d+)$")parts={}fordirpath,_,filenamesinos.walk(start_dir):forfninfilenames:m=pattern.match(fn)ifm:idx=int(m.group(1))if0<=idx<=49:withopen(os.path.join(dirpath,fn),"rb")asfh:parts[idx]=fh.read()missing=[iforiinrange(50)ifinotinparts]ifmissing:print(f"WARNING: missing indices {missing}",file=sys.stderr)returnb"".join(parts[i]foriinsorted(parts))if__name__=="__main__":start=sys.argv[1]iflen(sys.argv)>1else"lego_bricks_challenge"flag=solve(start)print(flag.decode("utf-8",errors="replace"))
Before running it, we verified all 50 required indices (0–49) existed somewhere in the tree with a quick scan — confirmed
Missing indices: []
,
Found count: 50
.
Running the reconstruction script:
python3 reconstruct_flag.py lego_bricks_challenge
bronco{3ve4yth1ng_1s_aw3s0me}
Flag
bronco{3ve4yth1ng_1s_aw3s0me}
Takeaways
Sort by size, not by name.
In a haystack-style challenge, the real content is usually one clear statistical outlier once you strip away the noise (
find ... -printf '%s %p\n' | sort -n
).
Hidden/dot-prefixed files deserve a look
—
.solver.py
was intentionally invisible to a plain
ls
.
Read the source before running it.
The script was self-documenting (docstring told us exactly what to do), which made writing an independent implementation trivial once the provided script hit an encoding snag.
Don't trust file content blindly.
A stray non-UTF-8 byte broke the original script's execution but not its usefulness as a spec — worth distinguishing "this script is broken" from "this script's logic is wrong."
Published: Sun, 12 Jul 2026 16:59:15 +0000 | Source: DEV Community
The World Cup quarter-finals are on right now. I'm watching matches, yelling at my screen, and I thought: what if I could bottle that feeling into an audio clip?
PassionCast
lets you pick your team, choose a passion mode (hype speech, glory moment, rivalry fire, heartbreak, fan anthem, or custom), and generates a 60-second AI commentary clip. Not the generic "your team is amazing" stuff. Actual references to your team's players, history, and rivals.
Pick India, select "Rivalry Fire," hit generate. You get a breathless commentator talking about the India-Pakistan cricket-meets-football tension, with real names and real context. Try Argentina and you get Maradona, La Albiceleste, the Diego legacy. That's what I was going for.
I specifically didn't want a backend here. The whole point is that someone can fork this, drop in their own keys, and have it running in 30 seconds.
The Gemini Part (where I spent most of my time)
Getting Gemini to write
passionate
content was harder than I expected. At temperature 0.7, everything came out bland. "Your team has a proud history. The fans are excited." Useless.
At
0.9
, things got interesting. Combined with a detailed system prompt that includes the team name, their rivals, their football culture, and the specific style I want (narrator vs commentator vs poet), the output started feeling real.
The trick was constraining length. I needed 150-200 words, the sweet spot for 45-60 seconds of audio. Too short and it feels empty. Too long and ElevenLabs starts rushing or the clip drags.
// Note: ${apiKey} is a template literal variable, not a real key. // Users provide their own free key from aistudio.google.com/apikeyconstresponse=awaitfetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({contents:[{parts:[{text:prompt}]}],generationConfig:{temperature:0.9,maxOutputTokens:500}})});
The ElevenLabs Part (where the magic happens)
I tested default voice settings first. Sounded flat. Like a GPS reading football commentary.
The fix: crank down
stability to 0.4
(default is higher). This adds emotional variation. The voice wavers, speeds up, gets louder on key phrases. Exactly what you want for sports commentary.
Then
style to 0.6
for extra expressiveness, and
speaker boost on
for clarity.
Three voices, matched to content:
Brian
(dramatic narrator) for glory moments
Liam
(energetic) for rivalry and hype
George
(poetic storyteller) for heartbreak and anthems
I used flag images from flagcdn.com instead of emoji because emoji flags don't render on Windows for England, Wales, and Scotland (they show as black rectangles). Learned that the hard way after building the whole grid with emoji first.
Dark theme with gold/fire accents because... it's a passion app. Light mode would feel wrong.
What I'd Add
If I had another weekend: social sharing (let people post their clips), match-day mode that checks today's fixtures and auto-suggests hype content, and a gallery so you can hear what other fans generated. PRs welcome if you want to tackle any of these.
Prize Categories
Best Use of Google AI
: Gemini 2.0 Flash with temperature 0.9 and detailed cultural prompting. Produces scripts with real player names, real rivalry history, and team-specific fan culture for 48 nations. Three distinct writing styles (narrator, commentator, poet) via style instructions in the prompt.
Best Use of ElevenLabs
: Multilingual v2 model with deliberately tuned settings. Stability at 0.4 for emotional range (default sounds robotic for sports content). Style at 0.6 for expressiveness. Three voices paired to content types. The output sounds like broadcast commentary, not text-to-speech.
Published: Sun, 12 Jul 2026 16:57:52 +0000 | Source: DEV Community
$file secret.txt
secret.txt: ASCII text, with very long lines (479), with no line terminators
No further context is given — the challenge is to figure out the encoding
scheme from the data alone.
Recon
The tuples are almost all 2- or 3-element groups of small integers, plus
three literal tokens scattered through the stream:
{
,
}
, and
_
.
Small integer pairs, a
{
/
}
wrapper, and an obvious "flag format" hint
(
bronco{...}
) strongly suggested a
coordinate lookup cipher
— each
tuple indexes into some 2D reference grid, and the underscore/braces are
literal characters meant to survive the decode untouched.
The first four tuples are the key to spotting the scheme:
(4, 17), (2, 16), (2, 15), (4, 9)
Reading these as
(period, group)
coordinates on the
periodic table
:
Tuple
Period
Group
Element
Symbol
(4, 17)
4
17
Bromine
Br
(2, 16)
2
16
Oxygen
O
(2, 15)
2
15
Nitrogen
N
(4, 9)
4
9
Cobalt
Co
Concatenating the symbols:
Br
+
O
+
N
+
Co
=
BrONCo
→
"Bronco"
— matching the expected flag prefix exactly. Scheme confirmed.
Refining the scheme
Most tuples are 2-element
(period, group)
pairs and decode to a full
element symbol (1 or 2 letters). But a number of tuples have a
third
element, e.g.
(4, 17, 2)
. Decoding these as full symbols produced
garbled output, so the third number was tested as a
letter index
into
the 2-letter symbol:
(4, 17, 2)->Br, take letter #2->"r"
(2, 1, 2) ->Li, take letter #2->"i"
(3, 13, 1)->Al, take letter #1->"A"
This let single letters be pulled out of two-letter element symbols —
necessary because plain English text needs individual letters, not
2-character blocks, at most positions.
The literal tokens map directly:
_
→ space
{
/
}
→ literal brace (flag wrapper)
Decoding
Running the full tuple stream through this scheme (see
decode.py
)
produces:
BrONCo{MY FAVOriTe MeSSAGeS HAVe AT eleMeNT OF S[?9,6?]PriSe}
Nearly the entire message resolves cleanly into a coherent, on-theme
sentence — fitting, since the challenge itself hinges on the "surprise"
of finding a message hidden in periodic table coordinates:
MY FAVORITE MESSAGES HAVE
AN
ELEMENT OF
SURPRISE
Two positions didn't resolve automatically:
(9, 6)
— there is no period 9 on the periodic table (periods only
go 1–7), so this tuple has no valid lookup. Context makes the intended
letter obvious: the word is
S_PRISE
, which can only sensibly be
SURPRISE
, so the missing letter is
U
.
The word decoded as
AT
reads awkwardly; grammatically the
sentence wants
AN
("have an element of surprise"). This points to
a likely transcription slip in the source tuple for that word (a digit
that should differ from what was copied), rather than an error in the
decoding scheme itself — every other tuple in the file resolves
without issue.
Both anomalies are localized to specific known tuples and don't affect
confidence in the rest of the decode, which is fully self-consistent
across ~50 other tuples.
(Spaces converted to underscores to match standard flag formatting
conventions.)
Tools
decode.py
— standalone Python decoder implementing the scheme above.
Usage:
python3 decode.py secret.txt
Full source:
#!/usr/bin/env python3
"""
Periodic Table Cipher Decoder
==============================
Encoding scheme discovered from secret.txt:
- Each token is either:
* A literal character: { } _
* A tuple (period, group)-> full element symbol (1 or 2 letters)
* A tuple (period, group, index) -> a single letter from that symbol,
where index is 1-based (1st or 2nd letter)
- "_" represents a space / underscore separator in the final message
- "{" and "}" are literal brace characters (used for the flag wrapper)
Example:
(4,17) (2,16) (2,15) (4,9)->Br O N Co->"BrONCo" -> "Bronco"
Usage:
python3 decode.py secret.txt
"""importreimportsys# Standard periodic table symbols by (period, group), IUPAC 1-18 group numbering.
# Only positions that are actually populated on a real periodic table are included.
PERIODIC_TABLE={(1,1):'H',(1,18):'He',(2,1):'Li',(2,2):'Be',(2,13):'B',(2,14):'C',(2,15):'N',(2,16):'O',(2,17):'F',(2,18):'Ne',(3,1):'Na',(3,2):'Mg',(3,13):'Al',(3,14):'Si',(3,15):'P',(3,16):'S',(3,17):'Cl',(3,18):'Ar',(4,1):'K',(4,2):'Ca',(4,3):'Sc',(4,4):'Ti',(4,5):'V',(4,6):'Cr',(4,7):'Mn',(4,8):'Fe',(4,9):'Co',(4,10):'Ni',(4,11):'Cu',(4,12):'Zn',(4,13):'Ga',(4,14):'Ge',(4,15):'As',(4,16):'Se',(4,17):'Br',(4,18):'Kr',(5,1):'Rb',(5,2):'Sr',(5,3):'Y',(5,4):'Zr',(5,5):'Nb',(5,6):'Mo',(5,7):'Tc',(5,8):'Ru',(5,9):'Rh',(5,10):'Pd',(5,11):'Ag',(5,12):'Cd',(5,13):'In',(5,14):'Sn',(5,15):'Sb',(5,16):'Te',(5,17):'I',(5,18):'Xe',(6,1):'Cs',(6,2):'Ba',(6,3):'La',(6,4):'Hf',(6,5):'Ta',(6,6):'W',(6,7):'Re',(6,8):'Os',(6,9):'Ir',(6,10):'Pt',(6,11):'Au',(6,12):'Hg',(6,13):'Tl',(6,14):'Pb',(6,15):'Bi',(6,16):'Po',(6,17):'At',(6,18):'Rn',(7,1):'Fr',(7,2):'Ra',(7,3):'Ac',(7,4):'Rf',(7,5):'Db',(7,6):'Sg',(7,7):'Bh',(7,8):'Hs',(7,9):'Mt',(7,10):'Ds',(7,11):'Rg',(7,12):'Cn',(7,13):'Nh',(7,14):'Fl',(7,15):'Mc',(7,16):'Lv',(7,17):'Ts',(7,18):'Og',}# Matches: {, }, _, or a tuple like (4, 17) / (4, 17, 2)
TOKEN_RE=re.compile(r'\{|\}|_|\(\s*\d+\s*,\s*\d+\s*(?:,\s*\d+\s*)?\)')TUPLE_RE=re.compile(r'\(\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d+)\s*)?\)')defdecode(text:str)->str:output=[]formatchinTOKEN_RE.finditer(text):token=match.group(0)iftokenin('{','}'):output.append(token)continueiftoken=='_':output.append('')continuetm=TUPLE_RE.match(token)period,group,index=tm.groups()period,group=int(period),int(group)symbol=PERIODIC_TABLE.get((period,group))ifsymbolisNone:output.append(f'[?{period},{group}?]')continueifindexisNone:output.append(symbol)# full symbol
else:idx=int(index)if1<=idx<=len(symbol):output.append(symbol[idx-1])# single letter
else:output.append(f'[?{symbol}#{idx}?]')return''.join(output)defmain():iflen(sys.argv)!=2:print(f"Usage: {sys.argv[0]} <secret.txt>")sys.exit(1)withopen(sys.argv[1],'r')asf:raw=f.read()result=decode(raw)print("Decoded message:")print(result)if__name__=='__main__':main()
Key Takeaways
When a cipher's tuples don't line up with an obvious 1:1 substitution,
try mapping them onto a
real-world reference table
(periodic table,
keyboard layout, ASCII table, book/page/line, etc.) rather than assuming
a purely mathematical transform.
Confirming a scheme against a
small, checkable prefix
(here, the
bronco{
flag wrapper) before decoding the whole message saves a lot
of wasted effort chasing the wrong theory.
When a decode is 95% clean and self-consistent, isolated garbage
characters are usually a
transcription/OCR artifact
in the source
data rather than a flaw in the scheme — worth verifying against the
raw file before assuming the cipher logic itself is wrong.
Published: Sun, 12 Jul 2026 16:56:25 +0000 | Source: DEV Community
I run a small developer tools site, and the whole thing started with one annoyance: sending JSON arrays to GPT or Claude wastes a ridiculous amount of tokens on repeated keys.
Three records, and the keys id, name, role, and active appear three times each. Now imagine 500 records. You are paying your LLM provider to read the same four words 500 times.
TOON is a compact format that declares the keys once, then lists values row by row:
users[3]{id,name,role,active}:
1,Alice,admin,true
2,Bob,editor,true
3,Carol,viewer,false
Same data. On my test sets this cuts tokens by 30 to 60 percent depending on how repetitive the data is. The bigger the array and the shorter the values, the bigger the saving, because keys make up a larger share of the payload.
The honest limitations
It only helps with arrays of similar objects. Deeply nested or mixed-shape data saves little or nothing.
The model needs one instruction line, something like: the following block is a compact table representation of JSON. Modern models handle it fine, but do not skip the hint.
If your prompt is mostly instructions and only a little data, the saving will not matter. This is for people stuffing hundreds of records into context.
The math
Say you push 10 million input tokens a month through GPT-4o at 2.50 dollars per million. If 60 percent of that is tabular JSON and TOON cuts it by 45 percent, you save around 6.75 dollars per million on that share, roughly 480 dollars a year. Not life changing at small scale, but it compounds fast at higher volume, and it also frees context window, which is sometimes worth more than the money.
I built a free converter that does JSON to TOON and back, shows a token estimate for both formats, and runs entirely in the browser: jsontoonpro.com. While building it I ended up adding about 50 other client-side tools (formatters, Base64, hash generators), but the TOON converter is still the reason the site exists.
Happy to answer questions about the format or share the test data I used for the percentages.
#llm #json #ai #webdev
Published: Sun, 12 Jul 2026 16:54:40 +0000 | Source: DEV Community
Hi Everyone. Approximately a month ago, I developed a Bash script to streamline server access for myself and my colleagues. The feedback was constructive and encouraging, and it motivated me to refine the concept into something more robust.
Today, I am releasing KunciMasuk, a free desktop application that reimagines that Bash script as a polished, user-friendly tool.
My dream
I have long been interested in desktop application development, but I wanted to build something simple yet purposeful. I deliberately avoided the ubiquitous to-do application, which felt cliché, and briefly considered a Point-of-Sale (POS) system before concluding that its scope was too ambitious for a side project.
As with any project, I began with system design and architecture planning. I initially evaluated Ionic, but it did not fit the use case.
I then considered Flutter, yet my lack of production experience with it made it impractical. After exploring discussions on Reddit and Stack Overflow, I noted the strong enthusiasm for Rust and Tauri.
While I am eager to explore both in the future, the learning curve was too steep for this release cycle. I ultimately selected Electron.js, capitalizing on my existing knowledge with React.
Database
For data persistence, I initially considered SQLite for its reliability and minimal footprint. However, for a lightweight SSH launcher, it felt like overkill.
I opted for a local JSON-based datastore instead. When a friend asked why KunciMasuk does not use cloud storage, the answer was straightforward: privacy. This application is designed to be local-first. All credentials and configuration data remain on the user's machine, ensuring complete confidentiality and zero external dependencies.
Move from React to TypeScript
With the architecture settled, I began development using VS Code, React.js, and Tailwind CSS.
Approximately twenty percent into the build, I decided to treat this project as an opportunity to learn TypeScript.
My approach was deliberate: I built each component in React, then consulted my AI mentor to explain TypeScript patterns and refactor the code accordingly. This method accelerated my learning without surrendering ownership of the codebase.
How I use AI to teach me about TypeScript
Had I fully automated the development with AI, I could have shipped within two days. Instead, I spent three weeks coding intentionally, debugging manually, and internalizing every architectural decision.
KunciMasuk may not be the most feature-rich SSH launcher available, but it is a product I built with full understanding of its internals , and that is a milestone I am proud of.
Following the release of version 0.1.0 to a trusted circle of peers and colleagues, I incorporated their feedback and iterated rapidly. I am now pleased to open version 0.1.4 to the public.
** My goal is to make this project open source. But I don't have enough courage to publish it due to my TypeScript skills. I will open it later or sooner ;)
Published: Sun, 12 Jul 2026 16:53:41 +0000 | Source: DEV Community
.NET appears as large monolith where everything is working like a magic. But it is not, and even GC can be used outside of .NET runtime (obviously with caveat). When I was a kid, I always love disassemble things, to see how they works. Not always toys survive that exercise. But thanks to source control, .NET GC is safe from my hands. Hopefully lot of people like me, and will enjoy tearing down large system into pieces.
The Wall Street Journal says "an intense 27-year-old activist who had been leading sit-ins at OpenAI to protest the dangers of AI" was just part of a larger movement.
"The Bay Area's AI boom is drawing young disillusioned men and women to join the fight against it. They are upending their lives and leaving behind careers for think tanks, nonprofits and street protest groups."
Their cause is now riding a surge of anti-AI backlash. Many Americans are souring on the technology amid mass layoffs, data center sprawl, reports of chatbot-fueled attacks by unstable users and hacking tools that have panicked cybersecurity professionals. Seventy percent of U.S. adults believe AI will cost jobs, and 55% believe it will do more harm than good in their daily lives, according to a recent Quinnipiac University poll. But for activists on the front lines, the driving fear is often more dramatic: human extinction. They cling to dire predictions, like Geoffrey Hinton's. The Nobel laureate, dubbed the "godfather of AI" for his work on artificial neural networks, warns of a 10% to 20% chance AI will wipe out humans.
At its most extreme and troubling end, some believe they must stop an AI apocalypse by any means necessary. In April, an unknown assailant fired 13 shots at the home of an Indianapolis councilman, leaving a note: "no data centers." That same month, authorities arrested a 20-year-old Texas college student for an attack on OpenAI CEO Sam Altman's home in San Francisco, and charged him with attempted murder and arson. The student was carrying an anti-AI document with a section on "our impending extinction," according to a federal criminal complaint. He has pleaded not guilty and his lawyers have said his actions appear to have been driven by an "acute mental-health crisis, not a desire to harm."